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

#include "atsc30_utils.h"
#include "bits_op.h"
#include "atsc30_log.h"
#include "alp_parser.h"
#include "alp_packet.h"
#include "alp_priv_packet.h"
#include "alp_stream_manager.h"
#include "alp_rdt.h"
#include "alp_adaption.h"
#include "alp_lls.h"
#include "network_packet.h"
#include "alp_rohc_decomp.h"
#include "rohc_decomp_internal.h"

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

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------
#define ROHC_PACKET_UO_0_LENGTH_MIN	(1)
// TODO
#define ROHC_PACKET_LENGTH_MIN		(2)

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

#define ALP_PARSER_IS_IP_COMPRESSED(input_type) \
	((input_type) == PARSER_INPUT_TYPE_ROHC)
#define ALP_PARSER_IS_IP_UNCOMPRESSED(input_type) \
	((input_type) == PARSER_INPUT_TYPE_IP)

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

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

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

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static bool _alp_parser_get_sid_info(
		const alp_context_t *ctx,
		u8 *sid)
{
	bool sid_flag = false;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(sid == NULL))
		return false;

	*sid = 0;

	switch (ctx->alp_hdr.encap_type) {
	case ENCAP_TYPE_HEADER_MODE:
		if (ctx->alp_hdr.add_hdr.header_mode.SIF) {
			sid_flag = true;
			*sid = ctx->alp_hdr.add_hdr.header_mode.sub_strm_id.SID;
		}
		break;

	case ENCAP_TYPE_SEGMENT:
		if (ctx->alp_hdr.add_hdr.segment.SIF) {
			sid_flag = true;
			*sid = ctx->alp_hdr.add_hdr.segment.sub_strm_id.SID;
		}
		break;

	case ENCAP_TYPE_CONCATE:
		if (ctx->alp_hdr.add_hdr.concate.SIF) {
			sid_flag = true;
			*sid = ctx->alp_hdr.add_hdr.concate.sub_strm_id.SID;
		}
		break;

	default:
		break;
	}

	return sid_flag;
}

static bool _alp_parser_ip_pkt_payload_length_check(
		bool is_non_comp_udp_ip_pkt,
		const u16 pkt_len)
{
	if (is_non_comp_udp_ip_pkt) {
		const u32 udp_ip_hdr_len = sizeof(ipv4_header_t) + sizeof(udp_header_t);

		if (pkt_len < udp_ip_hdr_len) {
			ALP_PARSER_LOG_WARN(
				"pkt drop[udp_ip too short(%d) < udp_ip len (%d))\n",
				pkt_len,
				udp_ip_hdr_len);

			return false;
		}
	} else {
		if (pkt_len < ROHC_PACKET_UO_0_LENGTH_MIN) {
			ALP_PARSER_LOG_WARN(
				"pkt drop[rohc pkt short(%d) < rohc len (%d))\n",
				pkt_len,
				ROHC_PACKET_UO_0_LENGTH_MIN);

			return false;
		}
	}

	return true;
}

static ALP_RET_CODE_T _alp_parser_alp_packet_filter(
		alp_context_t *ctx,
		net_buf_t data)
{
	alp_per_plp_context_t *ppx = NULL;
	bool is_non_comp_udp_ip_pkt = false;
	alp_stream_info_ext_t ext_info = { 0 };

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;

	is_non_comp_udp_ip_pkt = (ctx->alp_hdr.pkt_type == ALP_PACKET_IPV4);
	ppx = ctx->plp_ctx.cur;
	ppx->cur_strm_idx = MAX_SUPPORT_STREAM_NUMBER; //reset current plp strm idx

	if (!ppx->valid)
		return ALP_FAIL_NO_PLP_CONTEXT;
	if (!_alp_parser_ip_pkt_payload_length_check(is_non_comp_udp_ip_pkt, data.len))
		return ALP_FILTER_OUT;

	ext_info = (alp_stream_info_ext_t) {
		.compressed = (!is_non_comp_udp_ip_pkt),
		.sid_flag = false,
		.sid = 0,
		.plp_id = ppx->plp_id,
		.single_plp_flag = ctx->plp_ctx.single_plp_mode.valid,
	};

	ext_info.sid_flag = _alp_parser_get_sid_info(ctx, &ext_info.sid);

	if (!alp_lls_find_stream(ppx, &ctx->strm_mgr, data, ext_info)) {
		ALP_PARSER_LOG_WARN(
			"%s, alp_lls_find_stream fail\n",
			__func__);

		return ALP_FAIL_FILTER_ERROR;
	}

	if (ppx->cur_strm_idx >= MAX_SUPPORT_STREAM_NUMBER) {
		ALP_PARSER_LOG_TRACE("a drop[no match strm]\n");

		return ALP_FILTER_OUT;
	}

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T _alp_parser_ip_packet_filter(
		alp_context_t *ctx,
		net_buf_t ip_pkt,
		const alp_parser_input_info_t *input_info)
{
	alp_per_plp_context_t *ppx = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;

	ppx = ctx->plp_ctx.cur;

	if (!ppx->valid)
		return ALP_FAIL_NO_PLP_CONTEXT;
	else if (!_alp_parser_ip_pkt_payload_length_check(
			ALP_PARSER_IS_IP_UNCOMPRESSED(
				input_info->input_type),
			ip_pkt.len))
		return ALP_FILTER_OUT;
	else if (ppx->cur_strm_idx >= MAX_SUPPORT_STREAM_NUMBER) {
		alp_stream_info_ext_t ext_info = {
			.compressed = ALP_PARSER_IS_IP_COMPRESSED(
						input_info->input_type),
			.sid_flag = input_info->sid_flag,
			.sid = input_info->sid,
			.plp_id = ppx->plp_id,
			.single_plp_flag = ctx->plp_ctx.single_plp_mode.valid,
		};

		if (!alp_lls_find_stream(ppx, &ctx->strm_mgr, ip_pkt, ext_info)) {
			ALP_PARSER_LOG_WARN(
				"%s, alp_lls_find_stream fail\n",
				__func__);

			return ALP_FAIL_FILTER_ERROR;
		}
	}

	if (ppx->cur_strm_idx >= MAX_SUPPORT_STREAM_NUMBER) {
		ALP_PARSER_LOG_TRACE("drop [no match strm]\n");

		return ALP_FILTER_OUT;
	}

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T _alp_parser_handle_lls_payload(
		alp_context_t *ctx,
		net_buf_t lls_pkt)
{
	additional_header2_singaling_informaton_t *add_hdr2;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;

	add_hdr2 = &ctx->alp_hdr.add_hdr2.sig_info;

	switch (add_hdr2->type) {
	case LLS_TYPE_LMT:
		if (LLS_BINARY_NO_COMP(add_hdr2)) {
			if ((ctx->alp_parser_ops != NULL)
				&& (ctx->alp_parser_ops->output_lmt != NULL)) {
				lls_packet_descriptor_t lmt_desc = {
					.framer_idx = ctx->framer_idx,
					.plp_id = ctx->plp_ctx.cur->plp_id,
					.pkt_type = (u8) LLS_TYPE_LMT,
				};
				struct atsc30_profile profile = {
					.log_level = A30_DBG_LVL_INFO,
					.log_module = ATSC30_LOG_MODULE_PARSER,
					.log_prefix = __func__,
					.log_us = ATSC30_PROFILE_LOG_US_DEFAULT,
				};

				atsc30_profile_get_begin_time(&profile);
				if (!(*ctx->alp_parser_ops->output_lmt)
					(lls_pkt, &lmt_desc)) {
					ALP_PARSER_LOG_ENHANCED_ERROR(
						"output_lmt error\n");

					return ALP_FAIL_LLS_CB;
				}
				ATSC30_PROFILE_GET_END_TIME(&profile);
			}
			ALP_PARSER_LOG_DEBUG(
				"framer(%d) plp(%d) LMT received\n",
				ctx->framer_idx,
				ctx->plp_ctx.cur->plp_id);
		}
		break;

	case LLS_TYPE_RDT:
		if (LLS_BINARY_NO_COMP(add_hdr2))
			return alp_lls_handle_rdt_update(ctx->plp_ctx.cur, lls_pkt);

		break;

	default:
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"unknown sig type %d\n",
			add_hdr2->type);

		return ALP_FAIL_BAD_LLS_TYPE;
	}

	return ALP_SUCCESS;
}

__attribute__((unused))
static bool _alp_parser_comp_pkt_no_valid_RDT_attempt_guess_cid_type(
		alp_context_t *ctx,
		net_buf_t src_rohc_pkt)
{
	alp_per_plp_context_t *ppx = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return false;

	ppx = ctx->plp_ctx.cur;

	if (ppx->rdt.valid) {
		ALP_PARSER_LOG_VERBOS(
			"rdt already valid, no need to guess\n");

		return true;
	} else if (ppx->decomp_id >= MAX_DECOMPRESSOR_NUM) {
		u8 guess_max_cid = 0xF;

		ALP_PARSER_LOG_INFO("No RDT case Try Small CID case\n");
		ppx->decomp_id = alp_rohc_decomp_init(guess_max_cid);
		if (ppx->decomp_id >= MAX_DECOMPRESSOR_NUM) {
			ALP_PARSER_LOG_WARN(
				"%s fail to allocate rohc decompressor\n",
				__func__);

			return false;
		}

		net_buf_reset(&ppx->buf[PER_PLP_ROHC_BUF]);

		if (!alp_rohc_decompress(
			ppx->decomp_id,
			&ppx->buf[PER_PLP_ROHC_BUF],
			src_rohc_pkt)) {
			ALP_PARSER_LOG_DEBUG(
				"No RDT case, small cid guess fail Try Large CID case\n");
			guess_max_cid = 0x7F;
			alp_rohc_decomp_deinit(ppx->decomp_id);

			ppx->decomp_id = alp_rohc_decomp_init(guess_max_cid);
			if (ppx->decomp_id >= MAX_DECOMPRESSOR_NUM) {
				ALP_PARSER_LOG_WARN(
					"%s fail to allocate rohc decompressor\n",
					__func__);

				return false;
			}

			net_buf_reset(&ppx->buf[PER_PLP_ROHC_BUF]);
			if (!alp_rohc_decompress(
				ppx->decomp_id,
				&ppx->buf[PER_PLP_ROHC_BUF],
				src_rohc_pkt)) {
				ALP_PARSER_LOG_DEBUG(
					"No RDT case, large CID guess fail, release decompressor\n");
				alp_rohc_decomp_deinit(ppx->decomp_id);

				return false;
			}
		}

		ppx->rdt.valid = true;
		ppx->rdt.max_CID = guess_max_cid;
		rohc_parser_set_max_cid(&ppx->parser, guess_max_cid);
		ALP_PARSER_LOG_INFO(
			"No RDT guess cid type success, cid type%d\n",
			ppx->parser.cid_type);
	}

	return true;
}

static bool _alp_parser_decomp_rohc_packet(
		alp_context_t *ctx,
		net_buf_t *dst_ip_pkt,
		net_buf_t src_rohc_pkt)
{
	alp_per_plp_context_t *ppx = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return false;
	if (ALP_PARSER_VERIFY(dst_ip_pkt == NULL))
		return false;

	ppx = ctx->plp_ctx.cur;

	if (ppx->rdt.valid) {
		net_buf_reset(&ppx->buf[PER_PLP_ROHC_BUF]);

		if (ctx->sw_cfg.enable_sw_deadaption) {
			net_buf_t rohc_input_pkt = src_rohc_pkt;

			if (!alp_adaption_check(
				ctx->plp_ctx.cur,
				&rohc_input_pkt,
				src_rohc_pkt)) {
				ALP_PARSER_LOG_ENHANCED_ERROR(
					"check adaption fail\n");

				return false;
			}
		}

		if (ppx->decomp_id < MAX_DECOMPRESSOR_NUM) {
			if (!alp_rohc_decompress(
				ppx->decomp_id,
				&ppx->buf[PER_PLP_ROHC_BUF],
				src_rohc_pkt)) {
				ALP_PARSER_LOG_ENHANCED_ERROR(
					"alp_header_decompress fail\n");

				return false;
			}

			ALP_PARSER_LOG_TRACE(
				"decomp success, rohc pkt len %d, dst ip pkt len %d\n",
				src_rohc_pkt.len,
				ppx->buf[PER_PLP_ROHC_BUF].len);
		}

		*dst_ip_pkt = ppx->buf[PER_PLP_ROHC_BUF];
		ALP_PARSER_LOG_DEBUG(
			"plp[%d]-decomp[%d] done, ip pkt len %d\n",
			ppx->plp_id,
			ppx->decomp_id,
			dst_ip_pkt->len);
	}

	return true;
}

static bool _alp_parser_collect_udp_ip_cb_info(
		alp_context_t *ctx,
		udp_ip_packet_descriptor_t *desc,
		net_buf_t udp_ip_pkt)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return false;
	if (ALP_PARSER_VERIFY(desc == NULL))
		return false;
	if (ALP_PARSER_VERIFY(
		udp_ip_pkt.len < sizeof(ipv4_header_t) + sizeof(udp_header_t)))
		return false;

	if (ctx->plp_ctx.cur->valid) {
		network_udp_ip_t udpip;
		u8 idx = ctx->plp_ctx.cur->cur_strm_idx;

		if (idx >= MAX_SUPPORT_STREAM_NUMBER) {
			ALP_PARSER_LOG_ENHANCED_WARN(
				"invalid stream_idx(%d)\n",
				idx);

			return false;
		}

		desc->framer_idx = ctx->framer_idx;
		desc->plp_id = ctx->plp_ctx.cur->plp_id;
		desc->SID_flag = alp_strm_mgr_get_SID(&ctx->strm_mgr, &desc->SID, idx);
		desc->tag_id = alp_strm_mgr_get_tag(&ctx->strm_mgr, idx);

		if (!network_get_udp_ip(&udpip, udp_ip_pkt)) {
			ALP_PARSER_LOG_ENHANCED_ERROR(
				"network_get_udp_ip_nbo fail\n");

			return false;
		}

		desc->ihl = udpip.ihl;
		desc->src_addr = udpip.src_addr;
		desc->dst_addr = udpip.dst_addr;
		desc->src_port = udpip.src_port;
		desc->dst_port = udpip.dst_port;

		ALP_PARSER_LOG_TRACE(
			"udp_ip cb: frm(%d) plp(%d), strm_idx %d, tag_id 0x%08x\n",
			ctx->framer_idx,
			desc->plp_id,
			ctx->plp_ctx.cur->cur_strm_idx,
			desc->tag_id);

		return true;
	}

	return false;
}

static ALP_RET_CODE_T _alp_parser_deliver_udp_ip_packet(
		alp_context_t *ctx,
		net_buf_t udp_ip_pkt)
{
	udp_ip_packet_descriptor_t desc;
	struct atsc30_profile profile = {
		.log_level = A30_DBG_LVL_INFO,
		.log_module = ATSC30_LOG_MODULE_PARSER,
		.log_prefix = __func__,
		.log_us = ATSC30_PROFILE_LOG_US_DEFAULT,
	};

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;

	_alp_parser_collect_udp_ip_cb_info(ctx, &desc, udp_ip_pkt);

	if ((ctx->alp_parser_ops != NULL)
		&& (ctx->alp_parser_ops->output_udp_ip != NULL)) {
		atsc30_profile_get_begin_time(&profile);
		if (!(*ctx->alp_parser_ops->output_udp_ip)
			(udp_ip_pkt, &desc)) {
			ALP_PARSER_LOG_ENHANCED_ERROR(
				"output_udp_ip fail\n");

			return ALP_CODE_FAIL_DATA_CB;
		}
		ATSC30_PROFILE_GET_END_TIME(&profile);
	}

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T _alp_parser_handle_single_comp_payload(
		alp_context_t *ctx,
		net_buf_t rohc_pkt)
{
	net_buf_t udp_ip_pkt;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;

	if (!_alp_parser_decomp_rohc_packet(ctx, &udp_ip_pkt, rohc_pkt))
		return ALP_FAIL_DECOMPRESS;

	if (udp_ip_pkt.len > 0)
		return _alp_parser_deliver_udp_ip_packet(ctx, udp_ip_pkt);

	return ALP_FILTER_OUT;
}

static ALP_RET_CODE_T _alp_parser_alp_packet_handle_single_payload(
		alp_context_t *ctx,
		net_buf_t payload)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;

	if (ALP_PAYLOAD_IS_IP(ctx->alp_hdr.pkt_type)) {
		ret = _alp_parser_alp_packet_filter(ctx, payload);
		if (ret != ALP_SUCCESS)
			return ret;
	}

	switch (ctx->alp_hdr.pkt_type) {
	case ALP_PACKET_IPV4:
		ret = _alp_parser_deliver_udp_ip_packet(ctx, payload);
		break;

	case ALP_PACKET_COMP_IPV4:
		ret = _alp_parser_handle_single_comp_payload(ctx, payload);
		break;

	case ALP_PACKET_LLS:
		ret = _alp_parser_handle_lls_payload(ctx, payload);
		break;

	case ALP_PACKET_MPEG2_TS:
	case ALP_PACKET_TYPE_EXT:
	default:
		ALP_PARSER_LOG_ERROR(
			"payload parse fail, unknown data type %d\n",
			ctx->alp_hdr.pkt_type);
		ret = ALP_FAIL_PACKT_TYPE_NOT_SUPPORT;
		break;
	}

	return ret;
}

static inline bool _alp_parser_seg_buf_len_enough_to_check_filter(
		alp_packet_type_t pkt_type,
		u16 seg_pkt_len)
{
	if (pkt_type == ALP_PACKET_IPV4) {
		if (seg_pkt_len >= sizeof(ipv4_header_t) + sizeof(udp_header_t))
			return true;
	} else if (pkt_type == ALP_PACKET_COMP_IPV4) {
		if (seg_pkt_len > ROHC_PACKET_LENGTH_MIN)
			return true;
	}

	return false;
}

static void _alp_parser_segment_packet_update_filter_state(
		alp_context_t *ctx)
{
	alp_per_plp_context_t *ppx;
	alp_segment_context_t *seg_ctx;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return;

	ppx = ctx->plp_ctx.cur;
	seg_ctx = &ppx->seg_ctx;

	if (seg_ctx->filter_state == SEG_FILTER_STATE_UNKNOWN) {
		if (ALP_PAYLOAD_IS_IP(ctx->alp_hdr.pkt_type)) {
			if (_alp_parser_seg_buf_len_enough_to_check_filter(
				ctx->alp_hdr.pkt_type,
				ppx->buf[PER_PLP_SEG_BUF].len)) {
				ALP_RET_CODE_T filt_ret;

				filt_ret = _alp_parser_alp_packet_filter(
						ctx,
						ppx->buf[PER_PLP_SEG_BUF]);
				if (filt_ret == ALP_SUCCESS) {
					seg_ctx->strm_idx = ppx->cur_strm_idx;
					seg_ctx->filter_state = SEG_FILTER_STATE_IN;
				} else if (filt_ret == ALP_FILTER_OUT) {
					seg_ctx->filter_state = SEG_FILTER_STATE_OUT;
				}
			}
		} else {
			seg_ctx->strm_idx = MAX_SUPPORT_STREAM_NUMBER;
			seg_ctx->filter_state = SEG_FILTER_STATE_IN;
		}
	}
}

static ALP_RET_CODE_T _alp_parser_segment_packet_handle_last_segment(
		alp_context_t *ctx)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	alp_per_plp_context_t *ppx = NULL;
	alp_segment_context_t *seg_ctx = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;

	ppx = ctx->plp_ctx.cur;
	seg_ctx = &ppx->seg_ctx;

	switch (seg_ctx->filter_state) {
	case SEG_FILTER_STATE_IN:
		ALP_PARSER_LOG_DEBUG(
			"***last segment, tot seg data len 0x%x, part %d, strm_idx %d***\n",
			ppx->buf[PER_PLP_SEG_BUF].len,
			seg_ctx->next_seq_no,
			seg_ctx->strm_idx);

		ppx->cur_strm_idx = seg_ctx->strm_idx;
		ret = _alp_parser_alp_packet_handle_single_payload(
				ctx,
				ppx->buf[PER_PLP_SEG_BUF]);
		break;

	case SEG_FILTER_STATE_OUT:
		return ALP_FILTER_OUT;

	default:
		ALP_PARSER_LOG_WARN(
			"***last segment, tot seg data len 0x%x, fail to determine filter state***\n",
			ppx->buf[PER_PLP_SEG_BUF].len);
		return ALP_FILTER_OUT;
	}

	return ret;
}

static ALP_RET_CODE_T _alp_parser_alp_packet_handle_segment_payload(
		alp_context_t *ctx,
		net_buf_t payload)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	alp_per_plp_context_t *ppx = NULL;
	alp_segment_context_t *seg_ctx = NULL;
	additional_header_segment_t *add_hdr = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;

	ppx = ctx->plp_ctx.cur;
	seg_ctx = &ppx->seg_ctx;

	add_hdr = &ctx->alp_hdr.add_hdr.segment;

	ALP_PARSER_LOG_DEBUG(
		"***segment[%d]***\n",
		add_hdr->seq_no);
	if (add_hdr->seq_no == 0) {
		ALP_PARSER_LOG_DEBUG("***segment start***\n");

		seg_ctx->next_seq_no = 0;
		seg_ctx->filter_state = SEG_FILTER_STATE_UNKNOWN;
		seg_ctx->strm_idx = MAX_SUPPORT_STREAM_NUMBER;

		if (ALP_PARSER_VERIFY(add_hdr->last_seg_indicator == true))
			return ALP_ERROR;
		net_buf_reset(&ppx->buf[PER_PLP_SEG_BUF]);
	}
	if (add_hdr->seq_no != seg_ctx->next_seq_no) {
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"segment seq number error not continuous  cur %d, last %d\n",
			add_hdr->seq_no,
			seg_ctx->next_seq_no);

		return ALP_ERROR;
	}
	seg_ctx->next_seq_no++;

	if (seg_ctx->filter_state == SEG_FILTER_STATE_OUT)
		return ALP_FILTER_OUT;

	if (!net_buf_append_buf(&ppx->buf[PER_PLP_SEG_BUF], payload)) {
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"unable to append payload to segment buf\n");

		return ALP_SEGMENT_BUF_ERROR;
	}

	_alp_parser_segment_packet_update_filter_state(ctx);

	if (!add_hdr->last_seg_indicator)
		return ret;

	return _alp_parser_segment_packet_handle_last_segment(ctx);
}

static ALP_RET_CODE_T _alp_parser_alp_packet_handle_concate_payload(
		alp_context_t *ctx,
		net_buf_t payload)
{
	ALP_RET_CODE_T ret;

	u8 i;
	u16 pkt_len_sum = 0;

	additional_header_concate_t *add_hdr;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;

	if (NET_BUF_UNDERFLOW_CHECK(payload, ctx->alp_hdr.payload_length))
		return ALP_BUF_UNDERFLOW;

	add_hdr = &ctx->alp_hdr.add_hdr.concate;

	for (i = 0; i <= add_hdr->count + 1; ++i) {
		net_buf_t pkt_i = net_buf_get_sub_buf(
					payload, pkt_len_sum,
					add_hdr->compent_length[i]);

		ret = _alp_parser_alp_packet_handle_single_payload(ctx, pkt_i);

		if ((ret != ALP_SUCCESS) && (ret != ALP_FILTER_OUT))
			return ret;

		ALP_PARSER_LOG_DEBUG(
			"handle concate[%d] payload done, len 0x%x\n",
			i,
			add_hdr->compent_length[i]);
		pkt_len_sum += add_hdr->compent_length[i];
	}

	return ALP_SUCCESS;
}

static ALP_RET_CODE_T _alp_parser_alp_packet_handle_payload(
		alp_context_t *ctx,
		net_buf_t payload)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;

	switch (ctx->alp_hdr.encap_type) {
	case ENCAP_TYPE_SINGLE:
	case ENCAP_TYPE_HEADER_MODE:
		return _alp_parser_alp_packet_handle_single_payload(ctx, payload);

	case ENCAP_TYPE_SEGMENT:
		return _alp_parser_alp_packet_handle_segment_payload(ctx, payload);

	case ENCAP_TYPE_CONCATE:
		return _alp_parser_alp_packet_handle_concate_payload(ctx, payload);

	default:
		ALP_PARSER_LOG_ENHANCED_ERROR("unknown encap type\n");
	}

	return ALP_PARSE_ERR_INVALID_ENCAP_TYPE;
}

static inline bool _alp_parser_alp_packet_is_lmt(
		const alp_header_t *const alp_hdr)
{
	if (ALP_PARSER_VERIFY(alp_hdr == NULL))
		return false;

	if ((alp_hdr->pkt_type == ALP_PACKET_LLS)
		&& LLS_BINARY_NO_COMP(&alp_hdr->add_hdr2.sig_info)
		&& (alp_hdr->add_hdr2.sig_info.type == LLS_TYPE_LMT))
		return true;

	return false;
}

static ALP_RET_CODE_T _alp_parser_alp_packet_handle(
		alp_context_t *ctx,
		net_buf_t alp_pkt,
		const alp_parser_input_info_t *input_info)
{
	ALP_RET_CODE_T ret;
	net_buf_t payload;
	alp_parser_ptp_update_stc_cb ptp_update_stc_cb  = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;

	ret = alp_parse_headers(&ctx->alp_hdr, &alp_pkt);
	if (ret != ALP_SUCCESS)
		return ret;

	if (alp_pkt.len < ctx->alp_hdr.payload_length)
		return ALP_BUF_UNDERFLOW;

	if ((ctx->sw_cfg.enable_sw_ptp_update)
		&& (ctx->alp_parser_ops != NULL))
		ptp_update_stc_cb = ctx->alp_parser_ops->update_stc;
	ret = alp_priv_packet_check(
			&ctx->plp_ctx,
			&ctx->alp_hdr,
			ptp_update_stc_cb,
			ctx->alp_priv_packet_ops,
			input_info);
	if (ret != ALP_SUCCESS) {
		if ((ret == ALP_PRIVATE_PKT_PLP_ID)
			|| (ret == ALP_PRIVATE_PKT_PTP))
			return ALP_SUCCESS;

		return ret;
	}

	if ((!_alp_parser_alp_packet_is_lmt(&ctx->alp_hdr))
		&& ((ctx->plp_ctx.cur == NULL)
			|| (!ctx->plp_ctx.cur->valid))) {
		ALP_PARSER_LOG_INFO("wait for context to establish..\n");
		net_buf_consume(&alp_pkt, ctx->alp_hdr.payload_length);

		return ALP_WAIT_FOR_CONTEXT_TO_ESTABLISH;
	}
	payload = net_buf_init_full(
			net_buf_get_ptr(alp_pkt, 0),
			ctx->alp_hdr.payload_length);
	net_buf_consume(&alp_pkt, payload.len);
	ret = _alp_parser_alp_packet_handle_payload(ctx, payload);

	return ret;
}

static ALP_RET_CODE_T _alp_parser_ip_packet_handle(
		alp_context_t *ctx,
		net_buf_t ip_pkt,
		const alp_parser_input_info_t *input_info)
{
	ALP_RET_CODE_T ret;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(ctx->plp_ctx.cur == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;

	ctx->plp_ctx.cur->cur_strm_idx = input_info->strm_idx;

	ret = _alp_parser_ip_packet_filter(
			ctx,
			ip_pkt,
			input_info);
	if (ret == ALP_SUCCESS) {
		if (ALP_PARSER_IS_IP_UNCOMPRESSED(input_info->input_type))
			ret = _alp_parser_deliver_udp_ip_packet(ctx, ip_pkt);
		else if (ALP_PARSER_IS_IP_COMPRESSED(input_info->input_type))
			ret = _alp_parser_handle_single_comp_payload(ctx, ip_pkt);
		else {
			ALP_PARSER_LOG_ENHANCED_WARN(
				"input type(0x%x) is not IP or RoHC\n",
				input_info->input_type);
			ret = ALP_ERROR;
		}
	}

	// reset valid stream index for current pkt
	ctx->plp_ctx.cur->cur_strm_idx = MAX_SUPPORT_STREAM_NUMBER;

	return ret;
}

static bool _alp_parser_check_rohc_max_cid_update(
		alp_context_t *ctx,
		u8 max_cid)
{
	alp_per_plp_context_t *ppx = ctx->plp_ctx.cur;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;

	if ((ppx->decomp_id >= MAX_DECOMPRESSOR_NUM)
		&& (max_cid < MAX_CTX_NUM_PER_PLP)) {
		rohc_parser_set_max_cid(&ppx->parser, max_cid);
		ppx->decomp_id = alp_rohc_decomp_init(max_cid);
		ppx->rdt.valid = true;

		if (ppx->decomp_id >= MAX_DECOMPRESSOR_NUM) {
			ALP_PARSER_LOG_ENHANCED_INFO(
				"fail to allocate rohc decompressor\n");

			return false;
		}

		ALP_PARSER_LOG_INFO(
			"plp[%d] alloc rohc decomp_id[%d]\n",
			ppx->plp_id,
			ppx->decomp_id);
	}

	return true;
}

static bool _alp_parser_check_private_packet(
		alp_context_t *ctx,
		net_buf_t pud_pkt,
		const alp_parser_input_info_t *input_info)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;
	alp_parser_ptp_update_stc_cb ptp_update_stc_cb = NULL;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;

	ret = alp_parse_headers(&ctx->alp_hdr, &pud_pkt);
	if (ret != ALP_SUCCESS)
		return false;

	if ((ctx->sw_cfg.enable_sw_ptp_update)
		&& (ctx->alp_parser_ops != NULL))
		ptp_update_stc_cb = ctx->alp_parser_ops->update_stc;
	ret = alp_priv_packet_check(
			&ctx->plp_ctx,
			&ctx->alp_hdr,
			ptp_update_stc_cb,
			ctx->alp_priv_packet_ops,
			input_info);
	if ((ret == ALP_PRIVATE_PKT_PLP_ID)
		|| (ret == ALP_PRIVATE_PKT_PTP))
		return true;

	return false;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
ALP_RET_CODE_T alp_parser_run(
		alp_context_t *ctx,
		net_buf_t packet,
		const alp_parser_input_info_t *input_info)
{
	ALP_RET_CODE_T ret = ALP_SUCCESS;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return ALP_NULL_PTR;
	if (ALP_PARSER_VERIFY(input_info == NULL))
		return ALP_NULL_PTR;

	if (input_info->input_type == PARSER_INPUT_TYPE_PUD) {
		if (!_alp_parser_check_private_packet(
			ctx,
			packet,
			input_info)) {
			ALP_PARSER_LOG_ENHANCED_ERROR("pud packet check fail\n");

			return ALP_ERROR;
		}

		return ALP_SUCCESS;
	} else if (!alp_lls_plp_context_switch(
			&ctx->plp_ctx,
			input_info->plp_id)) {
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"cannot switch to plp carrying stream\n");

		return ALP_FAIL_NO_PLP_CONTEXT;
	} else if ((!ctx->sw_cfg.enable_sw_parse_rdt)
			&& (input_info->max_cid > 0)
			&& (input_info->max_cid < MAX_CTX_NUM_PER_PLP)) {
		if (!_alp_parser_check_rohc_max_cid_update(ctx, input_info->max_cid)) {
			ALP_PARSER_LOG_ENHANCED_ERROR(
				"alp_check_rohc_max_cid_update fail\n");

			return ALP_ERROR;
		}
	}

	switch (input_info->input_type) {
	case PARSER_INPUT_TYPE_ALP:
		ret = _alp_parser_alp_packet_handle(ctx, packet, input_info);
		break;

	case PARSER_INPUT_TYPE_IP:
	case PARSER_INPUT_TYPE_ROHC:
		ret = _alp_parser_ip_packet_handle(ctx, packet, input_info);
		break;

	default:
		ret = ALP_ERROR;
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"input packet type %d not support\n",
			input_info->input_type);
		break;
	}

	return ret;
}

bool alp_parser_init(
		alp_context_t *ctx,
		u8 framer_idx,
		const struct alp_parser_operations *alp_parser_ops,
		const struct alp_priv_packet_operations *alp_priv_packet_ops,
		alp_sw_parser_config_t sw_cfg)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(
		ALP_PARSER_OPERATIONS_IS_INVALID(alp_parser_ops)))
		return false;
	if (ALP_PARSER_VERIFY(
		ALP_PRIV_PACKET_OPERATIONS_IS_INVALID(alp_priv_packet_ops)))
		return false;

	memset(ctx, 0, sizeof(alp_context_t));

	ctx->framer_idx = framer_idx;
	ctx->alp_parser_ops = alp_parser_ops;
	ctx->alp_priv_packet_ops = alp_priv_packet_ops;

	ctx->sw_cfg = sw_cfg;
	if (!alp_strm_mgr_init(&ctx->strm_mgr)) {
		ALP_PARSER_LOG_ERROR("alp_strm_mgr_init is failed\n");

		return false;
	}
	if (!alp_lls_plp_context_init(&ctx->plp_ctx)) {
		ALP_PARSER_LOG_ERROR("alp_lls_plp_context_init is failed\n");

		if (!alp_lls_plp_context_deinit(&ctx->plp_ctx)) {
			ALP_PARSER_LOG_ENHANCED_WARN(
				"alp_lls_plp_context_deinit is failed\n");
		}

		// TODO: release other resource

		return false;
	}

	ctx->initialized = true;

	ALP_PARSER_LOG_INFO(
		"alp_parser init, framer(%d) en_deadapt %d,  en_rdt_prs %d, sw_ptp%d\n",
		ctx->framer_idx,
		sw_cfg.enable_sw_deadaption,
		sw_cfg.enable_sw_parse_rdt,
		sw_cfg.enable_sw_ptp_update);

	return true;
}

bool alp_parser_deinit(alp_context_t *ctx)
{
	bool ret = true;
	u8 i;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;

	// Do this even deinit is failed partially.
	ctx->initialized = false;

	ctx->alp_parser_ops = NULL;
	ctx->alp_priv_packet_ops = NULL;

	for (i = 0; i < MAX_PLP_NUMBER_SUPPORTED; ++i) {
		if ((ctx->plp_ctx.ppx_array[i].valid)
			&& (ctx->plp_ctx.ppx_array[i].decomp_id < MAX_DECOMPRESSOR_NUM)) {
			rohc_decomp_release_decompressor(ctx->plp_ctx.ppx_array[i].decomp_id);
			ALP_PARSER_LOG_ENHANCED_INFO(
				"release rohc decomp(%d) for plp(%d)\n",
				ctx->plp_ctx.ppx_array[i].decomp_id,
				ctx->plp_ctx.ppx_array[i].plp_id);
		}
	}

	if (!alp_lls_plp_context_deinit(&ctx->plp_ctx)) {
		ALP_PARSER_LOG_ENHANCED_WARN(
			"alp_lls_plp_context_deinit is failed\n");

		ret = false;
	}

	return ret;
}

const alp_stream_t *alp_parser_get_stream(
		const alp_context_t *ctx,
		alp_stream_ID strm_id)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return NULL;

	return alp_strm_mgr_get_stream(&ctx->strm_mgr, strm_id);
}

alp_stream_ID alp_parser_set_stream(
		alp_context_t *ctx,
		const alp_stream_t *new_strm)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return INVALID_ALP_STREAM_ID;
	if (ALP_PARSER_VERIFY(new_strm == NULL))
		return INVALID_ALP_STREAM_ID;

	if (!alp_lls_plp_context_switch(&ctx->plp_ctx, new_strm->plp_id)) {
		ALP_PARSER_LOG_ENHANCED_ERROR(
			"cannot switch to plp carrying stream\n");

		return INVALID_ALP_STREAM_ID;
	}

	return alp_strm_mgr_set_stream(&ctx->strm_mgr, new_strm);
}

bool alp_parser_update_stream_state(
		alp_context_t *ctx,
		alp_stream_ID strm_id,
		ALP_STREAM_STATE_T state)
{
	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;

	return alp_strm_mgr_update_stream_state(&ctx->strm_mgr, strm_id, state);
}

bool alp_parser_set_plp_id_filter(
		alp_context_t *ctx,
		const u8 *plp_id_arr,
		u8 plp_cnt)
{
	u8 i;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_id_arr == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_cnt > eATSC30_HW_SUPPORT_PLP_NUM))
		return false;

	if (plp_cnt > 0)
		for (i = 0; i < plp_cnt; i++)
			if (!alp_lls_plp_context_switch(
				&ctx->plp_ctx,
				plp_id_arr[i])) {
				ALP_PARSER_LOG_ENHANCED_ERROR(
					"cannot switch to plp carrying stream\n");

				return false;
			}

	return true;
}

bool alp_parser_clear_plp_id_filter(
		alp_context_t *ctx,
		const u8 *plp_id_arr,
		u8 plp_cnt)
{
	u8 i;
	u8 valid_count = 0;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_id_arr == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_cnt > eATSC30_HW_SUPPORT_PLP_NUM))
		return false;

	for (i = 0; i < plp_cnt; i++) {
		valid_count =
			alp_strm_mgr_get_valid_count(
				&ctx->strm_mgr,
				false,
				plp_id_arr[i]);
		if (valid_count != 0) {
			ALP_PARSER_LOG_ENHANCED_DEBUG(
				"Don't release plp_id %d, because valid count = %d\n",
				plp_id_arr[i],
				valid_count);

			continue;
		}

		if (!alp_lls_plp_context_release(
			&ctx->plp_ctx,
			plp_id_arr[i])) {
			ALP_PARSER_LOG_ENHANCED_ERROR(
				"Cannot release plp_id %d\n",
				plp_id_arr[i]);

			return false;
		}
	}

	return true;
}

void alp_parser_clear_all_plp_id_filters(alp_context_t *ctx)
{
	u8 i;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return;

	if (ctx->plp_ctx.plp_num > 0)
		for (i = 0; i < MAX_PLP_NUMBER_SUPPORTED; i++)
			if (ctx->plp_ctx.ppx_array[i].valid) {
				ALP_PARSER_LOG_DEBUG(
					"release plp(%d)\n",
					ctx->plp_ctx.ppx_array[i].plp_id);
				alp_lls_plp_context_release(
					&ctx->plp_ctx,
					ctx->plp_ctx.ppx_array[i].plp_id);
			}
}

bool alp_parser_get_plp_id(
		const alp_context_t *ctx,
		u8 *plp_id_arr,
		u8 plp_cnt_max)
{
	u8 i;
	u8 plp_cnt = 0;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_id_arr == NULL))
		return false;
	if (ALP_PARSER_VERIFY(plp_cnt_max > eATSC30_HW_SUPPORT_PLP_NUM))
		return false;
	if (ALP_PARSER_VERIFY(plp_cnt_max < ctx->plp_ctx.plp_num))
		return false;

	for (i = 0; i < MAX_PLP_NUMBER_SUPPORTED; i++) {
		if (ctx->plp_ctx.ppx_array[i].valid)
			plp_id_arr[plp_cnt++] = ctx->plp_ctx.ppx_array[i].plp_id;
	}

	return true;
}

void alp_parser_collect_statistics(const alp_context_t *ctx)
{
	u8 i;

	if (ALP_PARSER_VERIFY(ctx == NULL))
		return;

	alp_strm_mgr_dump_all_strm(&ctx->strm_mgr);

	ALP_PARSER_LOG_DEBUG(
		"************plp info total PLP Num(%d)************\n",
		ctx->plp_ctx.plp_num);

	for (i = 0; i < MAX_PLP_NUMBER_SUPPORTED; i++)
		if (ctx->plp_ctx.ppx_array[i].valid) {
			ALP_PARSER_LOG_DEBUG(
				"plp[#%d], id (%d), decomp_id(%d)\n",
				i,
				ctx->plp_ctx.ppx_array[i].plp_id,
				ctx->plp_ctx.cur->decomp_id);
		}
}
