#include "rebuild_manager.h"
#include <assert.h>
#include <vector>

void RebuildManager::ParseNaluBySize(AVPacket &pkt)
{
	if (m_fileReader.IsAnnexb4Video())
	{
		return ParseNaluByAnnexB(pkt);
	}
	else
	{
		return ParseNaluByAvcc(pkt);
	}
}
void RebuildManager::ParseNaluByAvcc(AVPacket &pkt)
{
	int video_size = 0;
	uint8_t *pSize = (uint8_t *)&video_size;
	*pSize++ = pkt.data[3];
	*pSize++ = pkt.data[2];
	*pSize++ = pkt.data[1];
	*pSize++ = pkt.data[0];

	assert((pkt.size - 4) >= video_size);

	if ((pkt.size - 4) == video_size)
	{
		//m_videoPktQueue.PushPacket(pkt);
		CallBackWhiteBoardData(pkt);
	}
	else
	{
		uint8_t *pData = pkt.data;

		int data_size = pkt.size;

		uint8_t *pTmpData = new uint8_t[pkt.size];
		int pTmpDataSize = 0;

		do
		{
			int one_item_size = 0;

			pSize = (uint8_t *)&one_item_size;
			*pSize++ = pData[3];
			*pSize++ = pData[2];
			*pSize++ = pData[1];
			*pSize++ = pData[0];

			one_item_size = one_item_size + 4;

			if (pData[4] == 0x06 && pData[5] == 0x05)
			{
				if (pTmpDataSize > 0)
				{
					AddOnePacket(pkt, pTmpData, pTmpDataSize);

					pTmpDataSize = 0;
				}
				else
				{
					AddOnePacket(pkt, pData, one_item_size);

					pData = pData + one_item_size;

					data_size = data_size - one_item_size;
				}
			}
			else
			{
				memcpy(pTmpData + pTmpDataSize, pData, one_item_size);
				pTmpDataSize = pTmpDataSize + one_item_size;
				pData = pData + one_item_size;
				data_size = data_size - one_item_size;
			}
		} while (data_size > 0);

		if (pTmpDataSize > 0)
		{
			AddOnePacket(pkt, pTmpData, pTmpDataSize);

			pTmpDataSize = 0;
		}

		delete pTmpData;

		av_packet_unref(&pkt);
	}
}

void RebuildManager::ParseNaluByAnnexB(AVPacket &pkt)
{
	typedef struct
	{
		uint8_t *pos;
		int startcode_size;
		int len;
	}NaluInfo;
	std::vector<NaluInfo> vec;

	uint8_t *pData = pkt.data;
	int nLeftSize = pkt.size;

	do
	{
		if (pData[0] == 0x00 && pData[1] == 0x00 && (pData[2] == 0x01 || (pData[2] == 0x00 && pData[3] == 0x01)))
		{
			if (vec.size() > 0)
			{
				vec.back().len = pData - vec.back().pos;
			}

			NaluInfo info = { 0 };
			info.pos = pData;
			info.startcode_size = pData[2] ? 3 : 4;
			vec.push_back(info);

			pData = pData + info.startcode_size;
			nLeftSize = nLeftSize - info.startcode_size;
		}
		else
		{
			pData++;
			nLeftSize--;
		}

	} while (nLeftSize > 4);

	if (vec.size() > 0)
	{
		vec.back().len = pkt.data + pkt.size - vec.back().pos;
	}

	if (vec.size() == 1)
	{
		//m_videoPktQueue.PushPacket(pkt);
		CallBackWhiteBoardData(pkt);
		return;
	}

	uint8_t *pTmpData = new uint8_t[pkt.size];
	int nTmpDataSize = 0;

	for (int i = 0; i < vec.size(); i++)
	{
		NaluInfo *info = &vec[i];
		if (info->pos[info->startcode_size] == 0x06 && info->pos[info->startcode_size + 1] == 0x05)
		{
			if (nTmpDataSize > 0)
			{
				AddOnePacket(pkt, pTmpData, nTmpDataSize);
				nTmpDataSize = 0;
			}

			AddOnePacket(pkt, info->pos, info->len);
		}
		else
		{
			memcpy(pTmpData + nTmpDataSize, info->pos, info->len);
			nTmpDataSize = nTmpDataSize + info->len;
		}
	}

	if (nTmpDataSize > 0)
	{
		AddOnePacket(pkt, pTmpData, nTmpDataSize);
		nTmpDataSize = 0;
	}

	delete[]pTmpData;

	av_packet_unref(&pkt);
}
void RebuildManager::AddOnePacket(AVPacket &pkt, uint8_t *buffer, size_t size)
{
	AVPacket tmp_packet = { 0 };
	av_init_packet(&tmp_packet);
	av_new_packet(&tmp_packet, size);

	tmp_packet.pts = pkt.pts;
	tmp_packet.dts = pkt.dts;
	tmp_packet.stream_index = pkt.stream_index;
	tmp_packet.flags = pkt.flags;
	tmp_packet.duration = pkt.duration;
	tmp_packet.pos = pkt.pos;

	memcpy(tmp_packet.data, buffer, size);

	//m_videoPktQueue.PushPacket(tmp_packet);
	CallBackWhiteBoardData(tmp_packet);
}
int RebuildManager::Delete0x03FromNalu(uint8_t* src, uint8_t*dst, int src_length)
{
	int si, di;
	si = di = 0;
	while (si + 2 < src_length) {
		if (src[si + 2] > 3) {
			dst[di++] = src[si++];
			dst[di++] = src[si++];
		}
		else if (src[si] == 0 && src[si + 1] == 0) {
			if (src[si + 2] == 3) { //escape
				dst[di++] = 0;
				dst[di++] = 0;
				si += 3;
				continue;
			}
			else //next start code
				av_log(NULL, AV_LOG_ERROR, "this is next nal\n");
		}

		dst[di++] = src[si++];
	}
	while (si < src_length)
		dst[di++] = src[si++];
	return di;
}
bool RebuildManager::GetWhiteBoardData(AVPacket &pkt, CFrameInfo &frameInfo)
{
	int new_len = pkt.size;

	uint8_t *pDataStart = NULL; //pointer to 0x06 0x05

	if (m_fileReader.IsAnnexb4Video())
	{
		if (pkt.data[0] == 0x00 && pkt.data[1] == 0x00 &&
			((pkt.data[2] == 0x01 && pkt.data[3] == 0x06 && pkt.data[4] == 0x05) ||
			(pkt.data[2] == 0x00 && pkt.data[3] == 0x01 && pkt.data[4] == 0x06 && pkt.data[5] == 0x05)))
		{
			pDataStart = pkt.data[2] ? (pkt.data + 3) : (pkt.data + 4);
		}
	}
	else
	{
		if (pkt.data[4] == 0x06 && pkt.data[5] == 0x05)
		{
			pDataStart = pkt.data + 4;
		}
	}

	if (!pDataStart) return false;

	new_len = new_len - (pDataStart - pkt.data);

	char *tmp_data = new char[pkt.size];
	memset(tmp_data, 0, pkt.size);

	Delete0x03FromNalu((unsigned char *)pDataStart, (unsigned char *)tmp_data, new_len);

	unsigned char *sei = (unsigned char *)tmp_data + 2;
	int payload_size = 0;

	do
	{
		payload_size += *sei;
	} while (*sei++ == 0xff);

	const char *uuid = "27EEA68F2AD93D48";
	int uuid_len = 16;

	if (payload_size <= uuid_len) return false;

	if (memcmp(sei, uuid, uuid_len) != 0) return false;

	sei = sei + uuid_len;

	payload_size = payload_size - uuid_len;

	/*
	------------------------------------------------------------------------------------------
	| 06 | 05 | size | uuid(16BYTE) | sub type(1BYTE) | frame type(1BYTE) | data ... ......  |
	------------------------------------------------------------------------------------------
	*/
	char subType = *sei++;
	payload_size--;

	//°×°åÊý¾Ý
	char dataType = *sei++;
	payload_size--;

	frameInfo.InitFrame(payload_size);
	frameInfo.m_nDataSize = payload_size;
	frameInfo.m_mediaType = CFrameInfo::Frame_Type_WhiteBoard;
	frameInfo.m_nWidth = payload_size;

	memcpy(frameInfo.m_pData, sei, payload_size);

	delete[]tmp_data;

	return true;
}