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

#include "rohc_packet.h"
#include "atsc30_utils.h"
#include "atsc30_log.h"
#include "bits_op.h"
#include "net_buf.h"
#include "rohc_parser.h"
#include "rohc_crc.h"
#include "rohc_decomp_internal.h"

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

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

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

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

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

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

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static bool _rohc_parser_parse_uo_x_rand_fields(
		const rohc_parser_t *const parser,
		rohc_packet_info_t *const pkt_info,
		const u8 x_field_ofst,
		const net_buf_t rohc_pkt)
{
	if (ATSC30_NULL_PTR_CHECK(pkt_info))
		return false;
	if (ATSC30_NULL_PTR_CHECK(parser))
		return false;

	if (parser->dyn_flags.rnd
		|| parser->dyn_flags.en_udp_csum) {
		u8 ofst = 0;

		if (parser->dyn_flags.rnd
			&& parser->dyn_flags.en_udp_csum)
			pkt_info->tot_header_len += 4;
		else
			pkt_info->tot_header_len += 2;

		if (net_buf_data_size(rohc_pkt) < pkt_info->tot_header_len) {
			ROHC_LOG_WARN(
				"%s prs_x_field buffer(size%d) not enough(need %d)\n",
				__func__,
				net_buf_data_size(rohc_pkt),
				pkt_info->tot_header_len);

			return false;
		}

		if (parser->dyn_flags.rnd) {
			pkt_info->ip_id =
				*(const u16 *)
				(net_buf_get_ptr(
					rohc_pkt,
					x_field_ofst));
			ofst += 2;
		}

		if (parser->dyn_flags.en_udp_csum) {
			pkt_info->udp_checksum =
				*(const u16 *)
				(net_buf_get_ptr(
					rohc_pkt,
					x_field_ofst + ofst));
		}
	}

	return true;
}

static bool _rohc_parser_parse_IR_DYN_header(
		rohc_packet_info_t *const pkt_info,
		const u8 crc_oct_ofst,
		const net_buf_t rohc_pkt)
{
	u8 hdr_crc;
	u8 cal_crc;
	u8 d_chain_start_idx =	crc_oct_ofst + 1;
	u8 ir_dyn_hdr[32];

	pkt_info->ctx.d_chain_len = ROHC_DYNAMIC_CHAIN_LEN;

	//cid(0 or 1)B + ir_dyn data type(1B) + profile(1B) + crc + dynamic chain
	pkt_info->tot_header_len = d_chain_start_idx + pkt_info->ctx.d_chain_len;

	if (ROHC_VERIFY(pkt_info->tot_header_len >= 32))
		return false;

	if (net_buf_data_size(rohc_pkt) < pkt_info->tot_header_len) {
		ROHC_LOG_WARN(
			"%s ir_dyn data net buffer(size%d) not enough(need %d)\n",
			__func__,
			net_buf_data_size(rohc_pkt),
			pkt_info->tot_header_len);

		return false;
	}

	// ir dyn header for CRC check
	memcpy(ir_dyn_hdr, net_buf_get_ptr(rohc_pkt, 0), pkt_info->tot_header_len);

	memcpy(pkt_info->ctx.d_chain, (ir_dyn_hdr + d_chain_start_idx), pkt_info->ctx.d_chain_len);

	// save original crc value in stream
	hdr_crc = ir_dyn_hdr[crc_oct_ofst];
	// clear crc field to do crc check
	ir_dyn_hdr[crc_oct_ofst] = 0;

	cal_crc = rohc_cal_crc(
			ROHC_CRC_TYPE_8,
			ir_dyn_hdr,
			pkt_info->tot_header_len,
			CRC_8_INIT_VAL);

	if (cal_crc != hdr_crc) {
		ROHC_LOG_WARN(
			"ir_dyn cal crc: 0X%02X  != hdr crc: 0X%02X\n",
			cal_crc,
			hdr_crc);

		return false;
	}

	return true;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
bool rohc_parser_parse_packet(
		const rohc_parser_t *const parser,
		rohc_packet_info_t *const pkt_info,
		const net_buf_t rohc_pkt)
{
	u8 non_cid_1st_oct_val = 0;
	u8 non_cid_2nd_oct_idx = 0;

	if (ATSC30_NULL_PTR_CHECK(pkt_info))
		return false;
	if (ATSC30_NULL_PTR_CHECK(parser))
		return false;

	pkt_info->cid_type = parser->cid_type;

	if (!rohc_packet_decode_rohc_cid(
			rohc_pkt,
			parser->cid_type,
			&pkt_info->cid,
			&non_cid_1st_oct_val,
			&non_cid_2nd_oct_idx)) {
		ROHC_LOG_ERROR(" fail to decode rohc cid\n");

		return false;
	}

	pkt_info->packet_type = rohc_packet_decode_packet_type(non_cid_1st_oct_val);

	switch (pkt_info->packet_type) {
	case ROHC_PACKET_IR_DYN:
		if (!_rohc_parser_parse_IR_DYN_header(
			pkt_info,
			non_cid_2nd_oct_idx + 1,
			rohc_pkt))
			return false;

		break;

	case ROHC_PACKET_UO_0:
		pkt_info->crc = ALP_GET_BITS_0_2(non_cid_1st_oct_val);
		pkt_info->tot_header_len = non_cid_2nd_oct_idx;

		if (!_rohc_parser_parse_uo_x_rand_fields(
			parser,
			pkt_info, non_cid_2nd_oct_idx,
			rohc_pkt))
			return false;

		break;

	case ROHC_PACKET_UO_1:
		pkt_info->crc =
			ALP_GET_BITS_0_2(
				net_buf_get_val(
					rohc_pkt,
					non_cid_2nd_oct_idx));
		pkt_info->tot_header_len = non_cid_2nd_oct_idx + 1;

		if (!_rohc_parser_parse_uo_x_rand_fields(
			parser,
			pkt_info,
			non_cid_2nd_oct_idx + 1,
			rohc_pkt))
			return false;

		break;

	case ROHC_PACKET_UOR_2:
		pkt_info->crc =
			ALP_GET_BITS_0_6(
				net_buf_get_val(
					rohc_pkt,
					non_cid_2nd_oct_idx));
		pkt_info->tot_header_len = non_cid_2nd_oct_idx + 1;
		if (!_rohc_parser_parse_uo_x_rand_fields(
			parser,
			pkt_info,
			non_cid_2nd_oct_idx + 1,
			rohc_pkt))
			return false;

		break;

	default:
		ROHC_LOG_ERROR(
			"%s: data type %s not support\n",
			__func__,
			rohc_packet_type_2_str(pkt_info->packet_type));

		return false;
	}

	ROHC_LOG_DEBUG(
		"%s, cid %d, rohc_pkt type %s, tot header length %d\n",
		__func__,
		pkt_info->cid,
		rohc_packet_type_2_str(pkt_info->packet_type),
		pkt_info->tot_header_len);

	return true;
}

void rohc_parser_set_max_cid(
		rohc_parser_t *const parser,
		const u8 max_cid)
{
	if (!parser->valid) {
		parser->valid = true;
		parser->max_cid = max_cid;
		parser->cid_type =
			(max_cid > ROHC_SMALL_CID_MAX)
				 ? ROHC_CID_TYPE_LARGE
				 : ROHC_CID_TYPE_SMALL;
		ROHC_LOG_INFO(
			"rohc parser set max cid %d\n",
			parser->max_cid);
	} else if (parser->max_cid != max_cid) {
		ROHC_LOG_INFO(
			"rohc parser change max cid from %d to %d\n",
			parser->max_cid,
			max_cid);
		parser->max_cid = max_cid;
		parser->cid_type =
			(max_cid > ROHC_SMALL_CID_MAX)
				 ? ROHC_CID_TYPE_LARGE
				 : ROHC_CID_TYPE_SMALL;
	}
}

void rohc_parser_set_dynamic_flags(
		rohc_parser_t *const parser,
		const rohc_dynamic_flags_t dyn_flags)
{
	if (parser != NULL)
		memcpy(&parser->dyn_flags,
			&dyn_flags,
			sizeof(rohc_dynamic_flags_t));
}

bool rohc_parser_parse_cid(
		const net_buf_t rohc_pkt,
		const rohc_cid_type_t cid_type,
		u8 *const cid)
{
	u8 oct1_data;

	if (ATSC30_NULL_PTR_CHECK(rohc_pkt.data))
		return false;
	if (ATSC30_NULL_PTR_CHECK(cid))
		return false;

	// does not consider empty packet of UO-0
	if (net_buf_data_size(rohc_pkt) < 2) {
		ROHC_LOG_ERROR(
			"%s, rohc buffer too short(less than 2)\n",
			__func__);

		return false;
	}

	oct1_data = net_buf_get_val(rohc_pkt, 0);

	if (cid_type == ROHC_CID_TYPE_SMALL) {
		if (ROHC_PACKET_IS_ADD_CID_OCT(oct1_data))
			*cid = ROHC_PACKET_GET_CID_FROM_ADD_CID_OCT(oct1_data);
		else
			*cid = 0;
	} else if (cid_type == ROHC_CID_TYPE_LARGE) {
		*cid = net_buf_get_val(rohc_pkt, 1);
	} else {
		ROHC_LOG_ERROR(
			"%s ALP only support 1 byte cid and not support svdl\n",
			__func__);
		return false;
	}

	return true;
}
