#include "mpeg4-vvc.h"
#include "mpeg4-bits.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>

#define H266_OPI		12
#define H266_DCI		13
#define H266_VPS		14
#define H266_SPS		15
#define H266_PPS		16
#define H266_AUD		20
#define H266_PREFIX_SEI 23
#define H266_SUFFIX_SEI 24

/*
* ISO/IEC 14496-15:2021 11.2.4.2.2 Syntax (p156)

aligned(8) class VvcPTLRecord(num_sublayers) {
	bit(2) reserved = 0;
	unsigned int(6) num_bytes_constraint_info;
	unsigned int(7) general_profile_idc;
	unsigned int(1) general_tier_flag;
	unsigned int(8) general_level_idc;
	unsigned int(1) ptl_frame_only_constraint_flag;
	unsigned int(1) ptl_multi_layer_enabled_flag;
	unsigned int(8*num_bytes_constraint_info - 2) general_constraint_info;
	for (i=num_sublayers - 2; i >= 0; i--)
		unsigned int(1) ptl_sublayer_level_present_flag[i];
	for (j=num_sublayers; j<=8 && num_sublayers > 1; j++)
		bit(1) ptl_reserved_zero_bit = 0;
	for (i=num_sublayers-2; i >= 0; i--)
		if (ptl_sublayer_level_present_flag[i])
			unsigned int(8) sublayer_level_idc[i];
	unsigned int(8) ptl_num_sub_profiles;
	for (j=0; j < ptl_num_sub_profiles; j++)
		unsigned int(32) general_sub_profile_idc[j];
}
*/

static int mpeg4_vvc_ptl_record_load(struct mpeg4_bits_t* bits, struct mpeg4_vvc_t* vvc)
{
	int i;
	mpeg4_bits_read_n(bits, 2); // reserved
	vvc->native_ptl.num_bytes_constraint_info = mpeg4_bits_read_uint32(bits, 6);
	vvc->native_ptl.general_profile_idc = mpeg4_bits_read_uint32(bits, 7);
	vvc->native_ptl.general_tier_flag = mpeg4_bits_read_uint32(bits, 1);
	vvc->native_ptl.general_level_idc = mpeg4_bits_read_uint32(bits, 8);
	for (i = 0; i < (int)vvc->native_ptl.num_bytes_constraint_info && i < sizeof(vvc->native_ptl.general_constraint_info)/sizeof(vvc->native_ptl.general_constraint_info[0]); i++)
	{
		vvc->native_ptl.general_constraint_info[i] = mpeg4_bits_read_uint8(bits, 8);
	}
	vvc->native_ptl.ptl_frame_only_constraint_flag = (vvc->native_ptl.general_constraint_info[0] & 0x80) ? 1 : 0;
	vvc->native_ptl.ptl_multi_layer_enabled_flag = (vvc->native_ptl.general_constraint_info[0] & 0x40) ? 1 : 0;

	vvc->native_ptl.ptl_sublayer_level_present_flag = 0;
	assert(vvc->num_sublayers >= 0 && vvc->num_sublayers <= 8);
	for (i = (int)vvc->num_sublayers - 2; i >= 0; i-=8)
		vvc->native_ptl.ptl_sublayer_level_present_flag = mpeg4_bits_read_uint8(bits, 8);

	for (i = (int)vvc->num_sublayers - 2; i >= 0 && i < sizeof(vvc->native_ptl.sublayer_level_idc)/sizeof(vvc->native_ptl.sublayer_level_idc[0]); i--)
	{
		if(vvc->native_ptl.ptl_sublayer_level_present_flag & (1 << i))
			vvc->native_ptl.sublayer_level_idc[i] = mpeg4_bits_read_uint8(bits, 8);
	}

	vvc->native_ptl.ptl_num_sub_profiles = mpeg4_bits_read_uint8(bits, 8);
	vvc->native_ptl.general_sub_profile_idc = (uint32_t*)(vvc->data + vvc->off);
	vvc->off += 4 * vvc->native_ptl.ptl_num_sub_profiles;
	for (i = 0; i < vvc->native_ptl.ptl_num_sub_profiles; i++)
	{
		vvc->native_ptl.general_sub_profile_idc[i] = mpeg4_bits_read_uint32(bits, 32);
	}

	return mpeg4_bits_error(bits);
}

static int mpeg4_vvc_ptl_record_save(struct mpeg4_bits_t* bits, const struct mpeg4_vvc_t* vvc)
{
	int i;
	mpeg4_bits_write_n(bits, 0, 2); // reserved
	mpeg4_bits_write_n(bits, vvc->native_ptl.num_bytes_constraint_info, 6);
	mpeg4_bits_write_n(bits, vvc->native_ptl.general_profile_idc, 7);
	mpeg4_bits_write_n(bits, vvc->native_ptl.general_tier_flag, 1);
	mpeg4_bits_write_n(bits, vvc->native_ptl.general_level_idc, 8);
	mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_frame_only_constraint_flag, 1);
	mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_multi_layer_enabled_flag, 1);
	for (i = 0; i < (int)vvc->native_ptl.num_bytes_constraint_info; i++)
	{
		mpeg4_bits_write_n(bits, vvc->native_ptl.general_constraint_info[i], i + 1 < (int)vvc->native_ptl.num_bytes_constraint_info ? 8 : 6);
	}

	assert(vvc->num_sublayers >= 0 && vvc->num_sublayers <= 8);
	for (i = (int)vvc->num_sublayers - 2; i >= 0; i -= 8)
		mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_sublayer_level_present_flag, 8);

	for (i = (int)vvc->num_sublayers - 2; i >= 0 && i < sizeof(vvc->native_ptl.sublayer_level_idc) / sizeof(vvc->native_ptl.sublayer_level_idc[0]); i--)
	{
		if (vvc->native_ptl.ptl_sublayer_level_present_flag & (1 << i))
			mpeg4_bits_write_uint8(bits, vvc->native_ptl.sublayer_level_idc[i], 8);
	}

	mpeg4_bits_write_uint8(bits, vvc->native_ptl.ptl_num_sub_profiles, 8);
	for (i = 0; i < vvc->native_ptl.ptl_num_sub_profiles; i++)
	{
		mpeg4_bits_write_uint32(bits, vvc->native_ptl.general_sub_profile_idc[i], 32);
	}

	return mpeg4_bits_error(bits);
}

/*
* ISO/IEC 14496-15:2021 11.2.4.2.2 Syntax (p156)

aligned(8) class VvcDecoderConfigurationRecord {
	bit(5) reserved = '11111'b;
	unsigned int(2) LengthSizeMinusOne;
	unsigned int(1) ptl_present_flag;
	if (ptl_present_flag) {
		unsigned int(9) ols_idx;
		unsigned int(3) num_sublayers;
		unsigned int(2) constant_frame_rate;
		unsigned int(2) chroma_format_idc;
		unsigned int(3) bit_depth_minus8;
		bit(5) reserved = '11111'b;
		VvcPTLRecord(num_sublayers) native_ptl;
		unsigned_int(16) max_picture_width;
		unsigned_int(16) max_picture_height;
		unsigned int(16) avg_frame_rate;
	}
	unsigned int(8) num_of_arrays;
	for (j=0; j < num_of_arrays; j++) {
		unsigned int(1) array_completeness;
		bit(2) reserved = 0;
		unsigned int(5) NAL_unit_type;
		if (NAL_unit_type != DCI_NUT  &&  NAL_unit_type != OPI_NUT)
			unsigned int(16) num_nalus;
		for (i=0; i< num_nalus; i++) {
			unsigned int(16) nal_unit_length;
			bit(8*nal_unit_length) nal_unit;
		}
	}
}
*/
int mpeg4_vvc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_vvc_t* vvc)
{
	struct mpeg4_bits_t bits;
	uint8_t nalutype;
	uint16_t i, j, k, n, numOfArrays;
	uint8_t* dst;

	if (bytes < 2)
		return -1;

	vvc->off = 0; // clear
	mpeg4_bits_init(&bits, (void*)data, bytes);
	mpeg4_bits_read_n(&bits, 5); // reserved '11111'b
	vvc->lengthSizeMinusOne = mpeg4_bits_read_uint32(&bits, 2);
	vvc->ptl_present_flag = mpeg4_bits_read(&bits);
	if (vvc->ptl_present_flag)
	{
		vvc->ols_idx = mpeg4_bits_read_uint32(&bits, 9);
		vvc->num_sublayers = mpeg4_bits_read_uint32(&bits, 3);
		vvc->constant_frame_rate = mpeg4_bits_read_uint32(&bits, 2);
		vvc->chroma_format_idc = mpeg4_bits_read_uint32(&bits, 2);
		vvc->bit_depth_minus8 = mpeg4_bits_read_uint32(&bits, 3);
		mpeg4_bits_read_n(&bits, 5); // reserved '11111'b
		mpeg4_vvc_ptl_record_load(&bits, vvc);
		vvc->max_picture_width = mpeg4_bits_read_uint16(&bits, 16);
		vvc->max_picture_height = mpeg4_bits_read_uint16(&bits, 16);
		vvc->avg_frame_rate = mpeg4_bits_read_uint16(&bits, 16);
	}

	if (0 != mpeg4_bits_error(&bits))
	{
		assert(0);
		return -1;
	}

	assert(0 == bits.bits % 8);
	dst = vvc->data + vvc->off;

	numOfArrays = mpeg4_bits_read_uint8(&bits, 8);
	for (i = 0; i < numOfArrays && 0 == mpeg4_bits_error(&bits); i++)
	{
		nalutype = mpeg4_bits_read_uint8(&bits, 8);
		
		n = 1;
		if ((nalutype & 0x1f) != H266_DCI && (nalutype & 0x1f) != H266_OPI)
			n = mpeg4_bits_read_uint16(&bits, 16);

		for (j = 0; j < n; j++)
		{
			if (vvc->numOfArrays >= sizeof(vvc->nalu) / sizeof(vvc->nalu[0]))
			{
				assert(0);
				return -E2BIG; // too many nalu(s)
			}

			k = mpeg4_bits_read_uint16(&bits, 16);
			vvc->nalu[vvc->numOfArrays].array_completeness = (nalutype >> 7) & 0x01;
			vvc->nalu[vvc->numOfArrays].type = nalutype & 0x1F;
			vvc->nalu[vvc->numOfArrays].bytes = k;
			vvc->nalu[vvc->numOfArrays].data = dst;
			memcpy(vvc->nalu[vvc->numOfArrays].data, data + bits.bits / 8, k);
			vvc->numOfArrays++;

			mpeg4_bits_skip(&bits, (uint64_t)k * 8);
			dst += k;
		}
	}

	vvc->off = (int)(dst - vvc->data);
	return mpeg4_bits_error(&bits) ? -1 : (int)(bits.bits / 8);
}

int mpeg4_vvc_decoder_configuration_record_save(const struct mpeg4_vvc_t* vvc, uint8_t* data, size_t bytes)
{
	uint16_t n;
	uint8_t i, j, k;
	uint8_t* ptr, * end;
	uint8_t* p;
	uint8_t array_completeness = 1;
	struct mpeg4_bits_t bits;
	const uint8_t nalu[] = { H266_OPI, H266_DCI, H266_VPS, H266_SPS, H266_PPS, H266_PREFIX_SEI, H266_SUFFIX_SEI };

	assert(vvc->lengthSizeMinusOne <= 3);
	memset(data, 0, bytes);
	mpeg4_bits_init(&bits, (void*)data, bytes);
	mpeg4_bits_write_n(&bits, 0x1F, 5);
	mpeg4_bits_write_n(&bits, vvc->lengthSizeMinusOne, 2);
	mpeg4_bits_write_n(&bits, vvc->ptl_present_flag, 1);

	if (vvc->ptl_present_flag)
	{
		mpeg4_bits_write_n(&bits, vvc->ols_idx, 9);
		mpeg4_bits_write_n(&bits, vvc->num_sublayers, 3);
		mpeg4_bits_write_n(&bits, vvc->constant_frame_rate, 2);
		mpeg4_bits_write_n(&bits, vvc->chroma_format_idc, 2);
		mpeg4_bits_write_n(&bits, vvc->bit_depth_minus8, 3);
		mpeg4_bits_write_n(&bits, 0x1F, 5);
		mpeg4_vvc_ptl_record_save(&bits, vvc);
		mpeg4_bits_write_uint16(&bits, vvc->max_picture_width, 16);
		mpeg4_bits_write_uint16(&bits, vvc->max_picture_height, 16);
		mpeg4_bits_write_uint16(&bits, vvc->avg_frame_rate, 16);
	}
	
	if (0 != mpeg4_bits_error(&bits) || 0 != bits.bits % 8)
	{
		assert(0);
		return -1;
	}

	//mpeg4_bits_write_uint8(&bits, vvc->numOfArrays, 8);
	p = data + bits.bits / 8 + 1 /*num_of_arrays*/;
	end = data + bytes;
	for (k = i = 0; i < sizeof(nalu) / sizeof(nalu[0]) && p + 5 <= end; i++)
	{
		ptr = p + 3;
		for (n = j = 0; j < vvc->numOfArrays; j++)
		{
			if (nalu[i] != vvc->nalu[j].type)
				continue;

			if (ptr + 2 + vvc->nalu[j].bytes > end)
				return 0; // don't have enough memory

			array_completeness = vvc->nalu[j].array_completeness;
			assert(vvc->nalu[i].data + vvc->nalu[j].bytes <= vvc->data + sizeof(vvc->data));
			ptr[0] = (vvc->nalu[j].bytes >> 8) & 0xFF;
			ptr[1] = vvc->nalu[j].bytes & 0xFF;
			memcpy(ptr + 2, vvc->nalu[j].data, vvc->nalu[j].bytes);
			ptr += 2 + vvc->nalu[j].bytes;
			n++;
		}

		if (n > 0)
		{
			// array_completeness + NAL_unit_type
			p[0] = (array_completeness << 7) | (nalu[i] & 0x1F);
			p[1] = (n >> 8) & 0xFF;
			p[2] = n & 0xFF;
			p = ptr;
			k++;
		}
	}

	data[bits.bits / 8] = k; // num_of_arrays

	return mpeg4_bits_error(&bits) ? -1 : (int)(p - data);
}

int mpeg4_vvc_to_nalu(const struct mpeg4_vvc_t* vvc, uint8_t* data, size_t bytes)
{
	uint8_t i;
	uint8_t* p, * end;
	const uint8_t startcode[] = { 0, 0, 0, 1 };

	p = data;
	end = p + bytes;

	for (i = 0; i < vvc->numOfArrays; i++)
	{
		if (p + vvc->nalu[i].bytes + 4 > end || vvc->nalu[i].bytes < 2)
			return -1;

		memcpy(p, startcode, 4);
		memcpy(p + 4, vvc->nalu[i].data, vvc->nalu[i].bytes);
		assert(vvc->nalu[i].type == ((vvc->nalu[i].data[1] >> 3) & 0x1F));
		p += 4 + vvc->nalu[i].bytes;
	}

	return (int)(p - data);
}

// RFC4648
static size_t base32_encode(char* target, const void* source, size_t bytes)
{
	size_t i, j;
	const uint8_t* ptr = (const uint8_t*)source;
	static const char* s_base32_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

	for (j = i = 0; i < bytes / 5 * 5; i += 5)
	{
		target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */
		target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/
		target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/
		target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/
		target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1) | ((ptr[i + 3] >> 7) & 0x01)]; /*c5*/
		target[j++] = s_base32_enc[(ptr[i + 3] >> 2) & 0x1F];/*c6*/
		target[j++] = s_base32_enc[((ptr[i + 3] & 0x03) << 3) | ((ptr[i + 4] >> 5) & 0x07)]; /*c7*/
		target[j++] = s_base32_enc[ptr[i + 4] & 0x1F]; /* c8 */
	}

	if (i + 1 == bytes)
	{
		target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */
		target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2)]; /*c2*/
	}
	else if (i + 2 == bytes)
	{
		target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */
		target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/
		target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/
		target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4)]; /*c4*/
	}
	else if (i + 3 == bytes)
	{
		target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */
		target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/
		target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/
		target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/
		target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1)]; /*c5*/
	}
	else if (i + 4 == bytes)
	{
		target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */
		target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/
		target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/
		target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/
		target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1) | ((ptr[i + 3] >> 7) & 0x01)]; /*c5*/
		target[j++] = s_base32_enc[(ptr[i + 3] >> 2) & 0x1F];/*c6*/
		target[j++] = s_base32_enc[((ptr[i + 3] & 0x03) << 3)]; /*c7*/
	}

	while (0 != (j % 8))
	{
		target[j++] = '=';
	}

	return j;
}

int mpeg4_vvc_codecs(const struct mpeg4_vvc_t* vvc, char* codecs, size_t bytes)
{
	// ISO/IEC 14496-15:2021
	// Annex E Sub-parameters of the MIME type "codecs" parameter (p276)
	// 'vvc1.' or 'vvi1.' prefix (5 chars)
	int i, n;
	char buffer[129];

	// 1. trailing zero bits of the general_constraint_info() syntax structure may be omitted from the input bits to base32 encoding
	n = (int)vvc->native_ptl.num_bytes_constraint_info;
	for (i = (int)vvc->native_ptl.num_bytes_constraint_info - 1; i >= 0 && n > 1; i--)
	{
		if (0 == vvc->native_ptl.general_constraint_info[i])
			n--;
		else
			break;
	}
	i = base32_encode(buffer, vvc->native_ptl.general_constraint_info, n);
	//2, the trailing padding with the "=" character may be omitted from the base32 string;
	while (i > 0 && buffer[i - 1] == '=') i--;

	return snprintf(codecs, bytes, "vvc1.%u.%c%u.C%.*s",
		(unsigned int)vvc->native_ptl.general_profile_idc,
		vvc->native_ptl.general_tier_flag ? 'H' : 'L', vvc->native_ptl.general_level_idc, i, buffer);
}

#if defined(_DEBUG) || defined(DEBUG)
void vvc_annexbtomp4_test(void);
static void mpeg4_vvc_codecs_test(struct mpeg4_vvc_t* vvc)
{
	int r;
	char buffer[129];
	//const char* s = "vvc1.1.L51.CQA.O1+3";
	//const char* s1 = "vvc1.17.L83.CYA.O1+3";
	//const char* s2 = "vvc1.17.L83.CYA.O1+3";
	const char* s3 = "vvc1.1.L105.CAA";
	r = mpeg4_vvc_codecs(vvc, buffer, sizeof(buffer));
	assert(r == strlen(s3) && 0 == memcmp(buffer, s3, r));
}

void mpeg4_vvc_test(void)
{
	const uint8_t data[] = { 0xff, 0x00, 0x11, 0x1f, 0x01, 0x02, 0x69, 0x00, 0x00, 0x02, 0xd0, 0x05, 0x00, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x01, 0x00, 0x2a, 0x00, 0x79, 0x00, 0x0b, 0x02, 0x69, 0x00, 0x00, 0x03, 0x00, 0x16, 0x88, 0x01, 0x40, 0x48, 0x80, 0x2b, 0x49, 0xff, 0x45, 0x19, 0x18, 0xe0, 0x0c, 0x42, 0x55, 0x5a, 0xab, 0xd5, 0xeb, 0x33, 0x25, 0x5a, 0x12, 0xe4, 0x72, 0xd4, 0x56, 0x5a, 0x32, 0x30, 0x40, 0x90, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x81, 0x00, 0x00, 0x0b, 0x44, 0x00, 0xa0, 0x22, 0x24, 0x18, 0x20 };
	uint8_t buffer[sizeof(data)];
	struct mpeg4_vvc_t vvc;
	memset(&vvc, 0, sizeof(vvc));
	assert(sizeof(data) == mpeg4_vvc_decoder_configuration_record_load(data, sizeof(data), &vvc));
	assert(3 == vvc.lengthSizeMinusOne && 1 == vvc.ptl_present_flag && 1 == vvc.num_sublayers);
	assert(1 == vvc.chroma_format_idc && 0 == vvc.bit_depth_minus8);
	assert(720 == vvc.max_picture_width && 1280 == vvc.max_picture_height && 0 == vvc.avg_frame_rate);
	assert(1 == vvc.native_ptl.num_bytes_constraint_info && 1 == vvc.native_ptl.general_profile_idc && 0 == vvc.native_ptl.general_tier_flag && 0x69 == vvc.native_ptl.general_level_idc);
	assert(2 == vvc.numOfArrays && H266_SPS == vvc.nalu[0].type && 0x2a == vvc.nalu[0].bytes && H266_PPS == vvc.nalu[1].type && 0x0c == vvc.nalu[1].bytes);
	assert(sizeof(data) == mpeg4_vvc_decoder_configuration_record_save(&vvc, buffer, sizeof(buffer)) && 0 == memcmp(buffer, data, sizeof(data)));
	mpeg4_vvc_codecs_test(&vvc);
}
#endif
