// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

#include "alp_packet.h"
#include "bits_op.h"
#include "atsc30_utils.h"
#include "atsc30_log.h"
#include "net_buf.h"

//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static inline const char *alp_packet_type_to_str(
		const alp_packet_type_t pkt_type)
{
	switch (pkt_type) {
	case ALP_PACKET_IPV4:
		return "IPV4";

	case ALP_PACKET_COMP_IPV4:
		return "COMP_IPV4";

	case ALP_PACKET_LLS:
		return "LLS";

	case ALP_PACKET_TYPE_EXT:
		return "TYPE_EXT";

	case ALP_PACKET_MPEG2_TS:
		return "MPEG2_TS";

	default:
		break;
	}

	return "BAD ALP PKT type";
}

static inline const char *alp_encap_type_to_str(
		const alp_encapsulation_type_t encap_type)
{
	switch (encap_type) {
	case ENCAP_TYPE_SINGLE:
		return "single";

	case ENCAP_TYPE_HEADER_MODE:
		return "head mode";

	case ENCAP_TYPE_SEGMENT:
		return "segment";

	case ENCAP_TYPE_CONCATE:
		return "concate";

	default:
		break;
	}

	return "BAD ALP ENCAP type";
}

static const char *link_layer_signaling_type_2_str(
		link_layer_signaling_type_t type)
{
	static char unknown_enum[ATSC30_LOG_UNKNOWN_ENUM_SIZE] = { 0 };
	static struct atsc30_log_enum_name_mapping names[] = {
		ATSC30_LOG_ENUM_ELEMENT(LLS_TYPE_LMT),
		ATSC30_LOG_ENUM_ELEMENT(LLS_TYPE_RDT),
		ATSC30_LOG_ENUM_ELEMENT(LLS_TYPE_UNKNOWN),
	};

	return atsc30_log_enum_2_str(
			names,
			ATSC30_LOG_NELEMS(names),
			unknown_enum,
			sizeof(unknown_enum),
			type);
}

static const char *link_layer_signaling_format_2_str(
		link_layer_signaling_format_t  format)
{
	static char unknown_enum[ATSC30_LOG_UNKNOWN_ENUM_SIZE] = { 0 };
	static const char names[][ATSC30_LOG_UNKNOWN_ENUM_SIZE] = {
		"LLS_FORMAT_BINARY",
		"LLS_FORMAT_XML",
		"LLS_FORMAT_JSON",
		"LLS_FORMAT_UNKNOWN",
	};

	return atsc30_log_enum_2_str_in_range(
			names,
			LLS_FORMAT_BINARY,
			LLS_FORMAT_UNKNOWN,
			unknown_enum,
			sizeof(unknown_enum),
			format);
}

static const char *link_layer_signaling_encoding_2_str(
		link_layer_signaling_encoding_t encoding)
{
	static char unknown_enum[ATSC30_LOG_UNKNOWN_ENUM_SIZE] = { 0 };
	static const char names[][ATSC30_LOG_UNKNOWN_ENUM_SIZE] = {
		"LLS_ENCODING_NO_COMP",
		"LLS_ENCODING_DEFLATE",
		"LLS_ENCODING_UNKNOWN",
	};

	return atsc30_log_enum_2_str_in_range(
			names,
			LLS_ENCODING_NO_COMP,
			LLS_ENCODING_UNKNOWN,
			unknown_enum,
			sizeof(unknown_enum),
			encoding);
}

static void dump_header_ext(
		int log_level,
		header_extension_t *hdr_ext)
{
	if (ALP_PARSER_VERIFY(hdr_ext == NULL))
		return;

	ALP_PARSER_LOG(log_level,
		"extension_type = 0x%x\n",
		hdr_ext->extension_type);
	ALP_PARSER_LOG(log_level,
		"extension_length_minus1 = 0x%x\n",
		hdr_ext->extension_length_minus1);

	atsc30_log_dump_hex_array(
		log_level,
		ATSC30_LOG_MODULE_PARSER,
		hdr_ext->extension_bytes,
		sizeof(hdr_ext->extension_bytes),
		sizeof(hdr_ext->extension_bytes[0]),
		"extension_bytes");
}

static void dump_header_mode(
		int log_level,
		additional_header_HM_t *header_mode)
{
	if (ALP_PARSER_VERIFY(header_mode == NULL))
		return;

	ALP_PARSER_LOG(log_level,
		"RSV = %s\n",
		ATSC30_BOOL_2_STR(header_mode->RSV));
	ALP_PARSER_LOG(log_level,
		"SIF = %s\n",
		ATSC30_BOOL_2_STR(header_mode->SIF));
	ALP_PARSER_LOG(log_level,
		"HEF = %s\n",
		ATSC30_BOOL_2_STR(header_mode->HEF));
	ALP_PARSER_LOG(log_level,
		"length_MSB = 0x%x\n",
		header_mode->length_MSB);
	ALP_PARSER_LOG(log_level,
		"sub_strm_id = 0x%x\n",
		header_mode->sub_strm_id.SID);

	dump_header_ext(
		log_level,
		&header_mode->hdr_ext);
}

static void dump_header_segment(
		int log_level,
		additional_header_segment_t *segment)
{
	if (ALP_PARSER_VERIFY(segment == NULL))
		return;

	ALP_PARSER_LOG(log_level,
		"last_seg_indicator = %s\n",
		ATSC30_BOOL_2_STR(segment->last_seg_indicator));
	ALP_PARSER_LOG(log_level,
		"SIF = %s\n",
		ATSC30_BOOL_2_STR(segment->SIF));
	ALP_PARSER_LOG(log_level,
		"HEF = %s\n",
		ATSC30_BOOL_2_STR(segment->HEF));
	ALP_PARSER_LOG(log_level,
		"seq_no = 0x%x\n",
		segment->seq_no);
	ALP_PARSER_LOG(log_level,
		"sub_strm_id = 0x%x\n",
		segment->sub_strm_id.SID);

	dump_header_ext(
		log_level,
		&segment->hdr_ext);
}

static void dump_header_concate(
		int log_level,
		additional_header_concate_t *concate)
{
	if (ALP_PARSER_VERIFY(concate == NULL))
		return;

	ALP_PARSER_LOG(log_level,
		"SIF = %s\n",
		ATSC30_BOOL_2_STR(concate->SIF));
	ALP_PARSER_LOG(log_level,
		"length_MSB = 0x%x\n",
		concate->length_MSB);
	ALP_PARSER_LOG(log_level,
		"count = %d\n",
		concate->count);

	atsc30_log_dump_hex_array(
		log_level,
		ATSC30_LOG_MODULE_PARSER,
		concate->compent_length,
		sizeof(concate->compent_length),
		sizeof(concate->compent_length[0]),
		"compent_length");

	ALP_PARSER_LOG(log_level,
		"sub_strm_id = 0x%x\n",
		concate->sub_strm_id.SID);
}

static void dump_additional_header(
		int log_level,
		alp_encapsulation_type_t encap_type,
		additional_header_u *add_hdr)
{
	if (ALP_PARSER_VERIFY(add_hdr == NULL))
		return;

	switch (encap_type) {
	case ENCAP_TYPE_HEADER_MODE:
		dump_header_mode(
			log_level,
			&add_hdr->header_mode);
		break;

	case ENCAP_TYPE_SEGMENT:
		dump_header_segment(
			log_level,
			&add_hdr->segment);
		break;

	case ENCAP_TYPE_CONCATE:
		dump_header_concate(
			log_level,
			&add_hdr->concate);
		break;

	default:
		break;
	}
}

static void dump_singaling_informaton(
		int log_level,
		additional_header2_singaling_informaton_t sig_info)
{
	ALP_PARSER_LOG(log_level,
		"sig_ver = %d\n",
		sig_info.sig_ver);
	ALP_PARSER_LOG(log_level,
		"sig_type_ext = 0x%x\n",
		sig_info.sig_type_ext);
	ALP_PARSER_LOG(log_level,
		"type = %s\n",
		link_layer_signaling_type_2_str(sig_info.type));
	ALP_PARSER_LOG(log_level,
		"format = %s\n",
		link_layer_signaling_format_2_str(sig_info.format));
	ALP_PARSER_LOG(log_level,
		"encoding = %s\n",
		link_layer_signaling_encoding_2_str(sig_info.encoding));
}

static void dump_type_ext(
		int log_level,
		additional_header2_type_extension_t type_ext)
{
	ALP_PARSER_LOG(log_level,
		"ext_type = 0x%x\n",
		type_ext.type_ext_hdr.ext_type);
}

static void dump_additional_header2(
		int log_level,
		alp_packet_type_t pkt_type,
		additional_header2_u *add_hdr2)
{
	if (ALP_PARSER_VERIFY(add_hdr2 == NULL))
		return;

	switch (pkt_type) {
	case ALP_PACKET_LLS:
		dump_singaling_informaton(
			log_level,
			add_hdr2->sig_info);
		break;

	case ALP_PACKET_TYPE_EXT:
		dump_type_ext(
			log_level,
			add_hdr2->type_ext);
		break;

	default:
		break;
	}
}

static inline ALP_RET_CODE_T parse_sid(
		sub_stream_identifidcation_t *sub_strm_id,
		net_buf_t *const sid_table)
{
	if (ATSC30_NULL_PTR_CHECK(sub_strm_id))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(sid_table))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*sid_table, 1))
		return ALP_BUF_UNDERFLOW;

	sub_strm_id->SID = net_buf_get_first_byte_val(*sid_table);
	net_buf_consume(sid_table, 1);

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T parse_header_extension(
		header_extension_t *const hdr_ext,
		net_buf_t *const ext_table)
{
	u16 i;
	u16 tot_len;

	if (ATSC30_NULL_PTR_CHECK(hdr_ext))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(ext_table))
		return ALP_NULL_PTR;

	/* 1B Extension_type + 1Byte Len_minus1 + (min1B) pkt */
	if (NET_BUF_UNDERFLOW_CHECK(*ext_table, 3))
		return ALP_BUF_UNDERFLOW;

	hdr_ext->extension_type = net_buf_get_first_byte_val(*ext_table);
	hdr_ext->extension_length_minus1 = net_buf_get_val(*ext_table, 1);

	tot_len = (u16)hdr_ext->extension_length_minus1 + 3;
	if (NET_BUF_UNDERFLOW_CHECK(*ext_table, tot_len))
		return ALP_BUF_UNDERFLOW;

	for (i = 2; i < tot_len; ++i) {
		hdr_ext->extension_bytes[i - 2] = net_buf_get_val(*ext_table, i);
		ALP_PARSER_LOG_TRACE(
			"parse hdr ext, byte[%d], val(%d)\n",
			i - 2,
			hdr_ext->extension_bytes[i - 2]);
	}

	net_buf_consume(ext_table, tot_len);

	ALP_PARSER_LOG_DEBUG(
		"parse hdr ext, type 0x%x, len_minus1 %d\n",
		hdr_ext->extension_type,
		hdr_ext->extension_length_minus1);

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T alp_parse_add_hdr_head_mode(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	u8 byte0;
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	additional_header_HM_t *HM = NULL;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 1))
		return ALP_BUF_UNDERFLOW;

	HM = &hdr->add_hdr.header_mode;

	byte0 = net_buf_get_first_byte_val(*alp_pkt);
	net_buf_consume(alp_pkt, 1);

	HM->length_MSB = ALP_GET_BITS_3_7(byte0);
	if (HM->length_MSB != 0)
		hdr->payload_length =
			ALP_HEADER_GET_PAYLOAD_LENGTH(
				HM->length_MSB,
				hdr->payload_length);

	HM->SIF = ALP_GET_BIT_1(byte0);
	if (HM->SIF) {
		ret = parse_sid(&HM->sub_strm_id, alp_pkt);

		if (ret != ALP_SUCCESS)
			return ret;
	}

	HM->HEF = ALP_GET_BIT_0(byte0);
	if (HM->HEF)
		ret = parse_header_extension(&HM->hdr_ext, alp_pkt);

	ALP_PARSER_LOG_DEBUG(
		"HM add hdr: len_MSB %d, tot_len %d, SIF %d, HEF %d\n",
		HM->length_MSB,
		hdr->payload_length,
		HM->SIF, HM->HEF);

	return ret;
}

static ALP_RET_CODE_T alp_parse_add_hdr_segment(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	u8 byte0;
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	additional_header_segment_t *seg = NULL;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 1))
		return ALP_BUF_UNDERFLOW;

	seg = &hdr->add_hdr.segment;

	byte0 = net_buf_get_first_byte_val(*alp_pkt);
	net_buf_consume(alp_pkt, 1);

	seg->seq_no = ALP_GET_BITS_3_7(byte0);
	seg->last_seg_indicator =  ALP_GET_BIT_2(byte0);

	seg->SIF = ALP_GET_BIT_1(byte0);
	if (seg->SIF) {
		ret = parse_sid(&seg->sub_strm_id, alp_pkt);

		if (ret != ALP_SUCCESS)
			return ret;
	}

	seg->HEF = ALP_GET_BIT_0(byte0);
	if (seg->HEF)
		ret = parse_header_extension(&seg->hdr_ext, alp_pkt);

	ALP_PARSER_LOG_DEBUG(
		"Seg Add hdr: Seq NO %d, last_ind %d\n",
		seg->seq_no,
		seg->last_seg_indicator);

	return ret;
}

//upceiling(count_plus1 * 1.5Bytes)
static inline u8 cal_concate_comp_len_field_size(u8 count_plus1)
{
	u8 comp_len_field_size;

	// TODO: default value?
	if (ALP_PARSER_VERIFY(
		count_plus1 > (MAX_CONCATE_PACKET_PART_NUMBER - 1)))
		return 0;

	comp_len_field_size = count_plus1 + (count_plus1 >> 1);

	if (count_plus1 & 0x1)
		comp_len_field_size += 1;

	return comp_len_field_size;
}

static ALP_RET_CODE_T alp_parse_add_hdr_concate(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	u8 tot_hdr_len_wo_sid = 1;
	ALP_RET_CODE_T ret = ALP_SUCCESS;

	u8 byte0;
	u8 byte1;
	u8 byte2;

	u8 i;
	u16 sum_payload_len = 0;
	u8 k = 1; // 1byte offset to component_length filed from length_MSB

	additional_header_concate_t *concate = NULL;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 1))
		return ALP_BUF_UNDERFLOW;

	concate = &hdr->add_hdr.concate;

	byte0 = net_buf_get_first_byte_val(*alp_pkt);

	concate->SIF = ALP_GET_BIT_0(byte0);

	concate->length_MSB = ALP_GET_BITS_4_7(byte0);
	hdr->payload_length =
		ALP_CONCATE_GET_PAYLOAD_LENGTH(
			concate->length_MSB,
			hdr->payload_length);

	concate->count = ALP_GET_BITS_1_3(byte0);

	ALP_PARSER_LOG_DEBUG(
		"concate pkt tot len %d, cnt %d\n",
		hdr->payload_length,
		concate->count + 2);

	tot_hdr_len_wo_sid += cal_concate_comp_len_field_size(concate->count + 1);
	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, tot_hdr_len_wo_sid))
		return ALP_BUF_UNDERFLOW;

	for (i = 0; i <= concate->count; i += 2) {
		bool last_odd = false;

		if ((i + 1) > concate->count)
			last_odd = true;

		byte0 = net_buf_get_val(*alp_pkt, k++);
		byte1 = net_buf_get_val(*alp_pkt, k++);

		if (!last_odd)
			byte2 = net_buf_get_val(*alp_pkt, k++);

		concate->compent_length[i] = ALP_CONCATE_GET_ODD_COMP_LENGTH(byte0, byte1);
		sum_payload_len += concate->compent_length[i];

		ALP_PARSER_LOG_DEBUG(
			"concate[%d] len %d\n",
			i,
			concate->compent_length[i]);

		if (!last_odd) {
			concate->compent_length[i + 1] =
				ALP_CONCATE_GET_EVEN_COMP_LENGTH(
					byte1,
					byte2);
			sum_payload_len += concate->compent_length[i + 1];
			ALP_PARSER_LOG_DEBUG(
				"concate[%d] len %d\n",
				i + 1,
				concate->compent_length[i + 1]);
		} else {
			// stuffing bits must be "0x0000"
			if (ALP_PARSER_VERIFY((byte1 & 0x0F) != 0x0))
				return ALP_ERROR;
		}
	}

	net_buf_consume(alp_pkt, tot_hdr_len_wo_sid);

	//the last component length = total len - all foregoing component length
	//if (ALP_PARSER_VERIFY(hdr->payload_length <= sum_payload_len))
	//	return ALP_ERROR;

	if (hdr->payload_length <= sum_payload_len) {
		ALP_PARSER_LOG_WARN(
			"concate pkt error, hdr_payload len%d, sum_payload_len(%d)\n",
			hdr->payload_length,
			sum_payload_len);

		return ALP_ERROR;
	}

	concate->compent_length[concate->count + 1] =
		hdr->payload_length - sum_payload_len;

	ALP_PARSER_LOG_DEBUG(
		"concate[%d] len %d, last pkt\n",
		concate->count + 1,
		concate->compent_length[concate->count + 1]);

	if (concate->SIF)
		ret = parse_sid(&concate->sub_strm_id, alp_pkt);

	ALP_PARSER_LOG_DEBUG(
		"con Add hdr: total Count %d\n",
		concate->count + 2);

	return ret;
}

static ALP_RET_CODE_T alp_parse_add_hdr2_lls(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	u8 byte4;
	u8 lls_type;
	additional_header2_singaling_informaton_t *add_hdr2;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 5))
		return ALP_BUF_UNDERFLOW;

	add_hdr2 = &hdr->add_hdr2.sig_info;

	lls_type = net_buf_get_first_byte_val(*alp_pkt);

	switch (lls_type) {
	case 0x01:
		add_hdr2->type = LLS_TYPE_LMT;
		break;

	case 0x02:
		add_hdr2->type = LLS_TYPE_RDT;
		break;

	default:
		add_hdr2->type = LLS_TYPE_UNKNOWN;
		ALP_PARSER_LOG_ERROR("unknown signal type %d\n", lls_type);
		return ALP_PARSE_ERROR_ADD_HDR2;
	}

	add_hdr2->sig_type_ext =
		ALP_GET_BYTES_CONCATENATED_U16(
			net_buf_get_val(*alp_pkt, 1),
			net_buf_get_val(*alp_pkt, 2));
	add_hdr2->sig_ver = net_buf_get_val(*alp_pkt, 3);

	byte4 = net_buf_get_val(*alp_pkt, 4);

	switch (ALP_GET_BITS_6_7(byte4)) {
	case 0x00:
		add_hdr2->format = LLS_FORMAT_BINARY;
		break;

	case 0x01:
		add_hdr2->format = LLS_FORMAT_XML;
		break;

	case 0x02:
		add_hdr2->format = LLS_FORMAT_JSON;
		break;

	default:
		add_hdr2->format = LLS_FORMAT_UNKNOWN;
		ALP_PARSER_LOG_ERROR("unknown signal fomrat %d\n",
			ALP_GET_BITS_6_7(byte4));
		return ALP_PARSE_ERROR_ADD_HDR2;
	}

	switch (ALP_GET_BITS_4_5(byte4)) {
	case 0x00:
		add_hdr2->encoding = LLS_ENCODING_NO_COMP;
		break;

	case 0x01:
		add_hdr2->encoding = LLS_ENCODING_DEFLATE;
		break;

	default:
		add_hdr2->encoding = LLS_ENCODING_UNKNOWN;
		ALP_PARSER_LOG_ERROR(
			"unknown signal encoding %d\n",
			ALP_GET_BITS_4_5(byte4));

		return ALP_PARSE_ERROR_ADD_HDR2;
	}

	if (ALP_GET_BITS_0_3(byte4) != 0x0F) {
		//reserved should be 1111

		ALP_PARSER_LOG_ERROR(
			"lls reserved field(0x%x) not 0xF\n",
			ALP_GET_BITS_0_3(byte4));

		return ALP_PARSE_ERROR_ADD_HDR2;
	}

	ALP_PARSER_LOG_DEBUG(
		"parse lls");
	dump_singaling_informaton(
		A30_DBG_LVL_DEBUG,
		*add_hdr2);

	net_buf_consume(alp_pkt, ALP_PARSE_LLS_HEADER_SIZE);

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T alp_parse_add_hdr2_type_ext(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	additional_header2_type_extension_t *add_hdr2;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 2))
		return ALP_BUF_UNDERFLOW;

	add_hdr2 = &hdr->add_hdr2.type_ext;

	add_hdr2->type_ext_hdr.ext_type =
		(((u32)net_buf_get_val(*alp_pkt, 0) << 8)
			| net_buf_get_val(*alp_pkt, 1));

	net_buf_consume(alp_pkt, ALP_PARSE_HEADER_EXT_SIZE);

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T alp_parse_add_hdr2(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	switch (hdr->pkt_type) {
	case ALP_PACKET_IPV4:
	case ALP_PACKET_COMP_IPV4:
		break;

	case ALP_PACKET_LLS:
		return alp_parse_add_hdr2_lls(hdr, alp_pkt);

	case ALP_PACKET_TYPE_EXT:
		return alp_parse_add_hdr2_type_ext(hdr, alp_pkt);

	default:
		ALP_PARSER_LOG_ERROR("no additional hdr2 for pkt type %d\n", hdr->pkt_type);
		return ALP_PARSE_ERROR_ADD_HDR2;
	}

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T alp_parse_add_hdr(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	switch (hdr->encap_type) {
	case ENCAP_TYPE_HEADER_MODE:
		return alp_parse_add_hdr_head_mode(hdr, alp_pkt);

	case ENCAP_TYPE_SEGMENT:
		return alp_parse_add_hdr_segment(hdr, alp_pkt);

	case ENCAP_TYPE_CONCATE:
		return alp_parse_add_hdr_concate(hdr, alp_pkt);

	case ENCAP_TYPE_SINGLE:
		break;

	default:
		ALP_PARSER_LOG_ERROR("parse Additional Hdr Error: Unkonwn alp encap type!!!\n");
		return ALP_PARSE_ERR_INVALID_ENCAP_TYPE;
	}

	return ALP_SUCCESS;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
ALP_RET_CODE_T alp_parse_base_header(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	u8 byte0;
	bool pc;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(*alp_pkt, 2))
		return ALP_BUF_UNDERFLOW;

	byte0 = net_buf_get_val(*alp_pkt, 0);

	hdr->pkt_type = ALP_GET_BASE_PACKET_TYPE(byte0);
	switch (hdr->pkt_type) {
	case ALP_PACKET_IPV4:
	case ALP_PACKET_COMP_IPV4:
	case ALP_PACKET_LLS:
	case ALP_PACKET_TYPE_EXT:
		break;

	case ALP_PACKET_MPEG2_TS:
		ALP_PARSER_LOG_WARN("ALP_TS not support\n");
		return ALP_PARSE_ERR_BASE_HDR;

	default:
		hdr->pkt_type = ALP_PACKET_UNKONWN;
		ALP_PARSER_LOG_ERROR(
			"unkonwn alp pkt type %d\n",
			ALP_GET_BASE_PACKET_TYPE(byte0));
		return ALP_PARSE_ERR_BASE_HDR;
	}

	pc = ALP_GET_BASE_PAYLOAD_CONFIG(byte0);

	if (pc) {
		bool sc = ALP_GET_BASE_SEGMENT_CONCAT(byte0);

		hdr->encap_type = (sc ? ENCAP_TYPE_CONCATE : ENCAP_TYPE_SEGMENT);
	} else {
		bool hm = ALP_GET_BASE_HEADER_MODE(byte0);

		hdr->encap_type = (hm ? ENCAP_TYPE_HEADER_MODE : ENCAP_TYPE_SINGLE);
	}

	hdr->length_lsb =
		ALP_GET_BASE_LENGTH_LSB(
			byte0,
			net_buf_get_val(*alp_pkt, 1));

	hdr->payload_length = hdr->length_lsb;

	net_buf_consume(alp_pkt, ALP_PARSE_BASE_HEADER_SIZE);

	ALP_PARSER_LOG_DEBUG(
		"alp base hdr, packet(%s), encap(%s), len_lsb %d\n",
		alp_packet_type_to_str(hdr->pkt_type),
		alp_encap_type_to_str(hdr->encap_type),
		hdr->length_lsb);

	return ALP_SUCCESS;
}

ALP_RET_CODE_T  alp_parse_headers(
		alp_header_t *const hdr,
		net_buf_t *const alp_pkt)
{
	ALP_RET_CODE_T ret;

	if (ATSC30_NULL_PTR_CHECK(hdr))
		return ALP_NULL_PTR;
	if (ATSC30_NULL_PTR_CHECK(alp_pkt))
		return ALP_NULL_PTR;

	memset(hdr, 0, sizeof(alp_header_t));

	if (alp_pkt->len >= 6)
		atsc30_log_dump_hex_array(
			A30_DBG_LVL_VERBOS,
			ATSC30_LOG_MODULE_ROHC,
			alp_pkt->data,
			6,
			sizeof(alp_pkt->data[0]),
			"ALP packet data dump 6 bytes");

	ret = alp_parse_base_header(hdr, alp_pkt);
	if (ret != ALP_SUCCESS)
		return ret;

	ret = alp_parse_add_hdr(hdr, alp_pkt);
	if (ret != ALP_SUCCESS)
		return ret;

	ret = alp_parse_add_hdr2(hdr, alp_pkt);
	if (ret != ALP_SUCCESS)
		return ret;

	if (alp_pkt->len < hdr->payload_length) {
		ALP_PARSER_LOG_ERROR(
			"input buffer not enough buf len %d < alp pkt size %d\n",
			alp_pkt->len,
			hdr->payload_length);

		ret = ALP_BUF_UNDERFLOW;
	}

	//offset means consume how many bytes in header
	hdr->pkt_length = hdr->payload_length + alp_pkt->offset;

	alp_dump_alp_header(
		A30_DBG_LVL_DEBUG,
		hdr);

	return ret;
}

void alp_dump_alp_header(
		int log_level,
		alp_header_t *const hdr)
{
	if (ATSC30_NULL_PTR_CHECK(hdr))
		return;

	ALP_PARSER_LOG(log_level,
		"payload_length = 0x%x\n",
		hdr->payload_length);
	ALP_PARSER_LOG(log_level,
		"pkt_length = 0x%x\n",
		hdr->pkt_length);
	ALP_PARSER_LOG(log_level,
		"length_lsb = 0x%x\n",
		hdr->length_lsb);
	ALP_PARSER_LOG(log_level,
		"pkt_type = %s\n",
		alp_packet_type_to_str(hdr->pkt_type));
	ALP_PARSER_LOG(log_level,
		"encap_type = %s\n",
		alp_encap_type_to_str(hdr->encap_type));

	dump_additional_header(
		log_level,
		hdr->encap_type,
		&hdr->add_hdr);

	dump_additional_header2(
		log_level,
		hdr->pkt_type,
		&hdr->add_hdr2);
}
