/*
*                    Copyright (c), NXP Semiconductors
*
*                       (C) NXP Semiconductors 2018
*
*         All rights are reserved. Reproduction in whole or in part is
*        prohibited without the written consent of the copyright owner.
*    NXP reserves the right to make changes without notice at any time.
*   NXP makes no warranty, expressed, implied or statutory, including but
*   not limited to any implied warranty of merchantability or fitness for any
*  particular purpose, or that the use will not infringe any third party patent,
*   copyright or trademark. NXP must not be liable for any loss or damage
*                            arising from its use.
*/


/* ********************************************************************************************
* Includes
* ****************************************************************************************** */

#include <stdio.h>
#include <string.h>
#include "NDEF_Message.h"
#include "Internal/NDEF_Heap.h"
#include "Internal/NDEF_RecordInternals.h"


/* ********************************************************************************************
* Definitions
* ******************************************************************************************* */
#define CHECK_OFFSET_VALIDITY(x) if(dwOffset+(x)>dwLength){ bErrorFlag=ERROR_OCURRED; break;}

/* ********************************************************************************************
* Global and Static Variables
* Total Size: NNNbytes
* ******************************************************************************************* */


/* ********************************************************************************************
* Private Functions Prototypes
* ******************************************************************************************* */
static NDEF_Message_t *  NDEF_Msg_Create(const void * pRecord);
static Status_t NDEF_Msg_UpdateHeaderFlags(NDEF_Message_t * pMessage);
static Status_t INT_UninitRecHook(void *pRecord, uint8_t * pOutput, uint32_t * pOffset);
static Status_t Msg_FreeFetchedHeaders(INT_FetchedHeader_t * pNode, uint16_t wNumOfRecords);

static Status_t(*const INT_SerializeRecord[TypeIdMax])(void *pRecord, uint8_t * pOutput, uint32_t * pOffset) =
{
	INT_UninitRecHook, INT_GenPayload_Serialize, INT_UninitRecHook, INT_BtsspBrEdr_Serialize, INT_BtsspLe_Serialize, INT_UninitRecHook, INT_UninitRecHook, INT_UninitRecHook, INT_Text_Serialize, INT_Uri_Serialize, INT_UninitRecHook, INT_UninitRecHook, INT_UninitRecHook
};


/* ********************************************************************************************
* Public Functions
* ******************************************************************************************* */

Status_t NDEF_Msg_AppendRecord(NDEF_Message_t ** ppMessage, const void * pRecord)
{
	NDEF_Message_t * pMessage;
	RecNode_t * pNode;

	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(ppMessage);
	if (*ppMessage == NULL)
	{

		pMessage = NDEF_Msg_Create(pRecord);
		*ppMessage = pMessage;
	}
	else
	{
		pMessage = *ppMessage;
		pNode = INT_MALLOC(sizeof(RecNode_t));
		RETURN_ON_NULL_MEMBER(pNode);
		pNode->pPrevNode = pMessage->pLastNode;
		pNode->pNextNode = NULL;
		pNode->pRecord =(void *) pRecord;
		pMessage->pLastNode->pNextNode = pNode;
		pMessage->pLastNode = pNode;

	}
	pMessage->wRecordsInMessage++;
	return NDEF_STATUS_SUCCESS;

}


Status_t NDEF_Msg_PrependRecord(NDEF_Message_t ** ppMessage, const void * pRecord)
{
	NDEF_Message_t * pMessage;
	RecNode_t * pNode;

	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(ppMessage);
	pMessage = *ppMessage;
	if (*ppMessage == NULL)
	{
		pMessage = NDEF_Msg_Create(pRecord);
		*ppMessage = pMessage;
	}
	else
	{
		pNode = INT_MALLOC(sizeof(RecNode_t));
		RETURN_ON_NULL_MEMBER(pNode);
		pNode->pNextNode = pMessage->pFirstNode;
		pNode->pPrevNode = NULL;
		pNode->pRecord = (void *)pRecord;
		pMessage->pFirstNode->pPrevNode = pNode;
		pMessage->pFirstNode = pNode;

	}
	pMessage->wRecordsInMessage++;
	return NDEF_STATUS_SUCCESS;
}



RecordTypeId_t NDEF_Msg_GetRecTypeByIndex(NDEF_Message_t * pMessage, uint16_t wIndex)
{
	NDEF_Rec_GenericCasting_t * pHandle;
	RecordTypeId_t retval = 0;
	if (pMessage == NULL)
	{
		retval = 0;
	}
	else if (wIndex < pMessage->wRecordsInMessage)
	{
		pHandle = (NDEF_Rec_GenericCasting_t *)NDEF_Msg_GetRecHandleByIndex(pMessage, wIndex);
		retval = pHandle->INT_sRecordInfo.eId;
	}
	return retval;

}


void * NDEF_Msg_GetRecHandleByIndex(const NDEF_Message_t * pMessage, uint16_t wIndex)
{
	void * retval = NULL;
	RecNode_t * pNode;
	uint16_t wCounter;
	RETURN_NULL_ON_NULL(pMessage);
	if (wIndex < pMessage->wRecordsInMessage)
	{
		pNode = pMessage->pFirstNode;
		for (wCounter = 0; wCounter < wIndex; wCounter++)
		{
			pNode = pNode->pNextNode;
		}
		retval = pNode->pRecord;
	}
	return retval;
}


uint16_t NDEF_Msg_GetNumberOfRecords(const NDEF_Message_t *  pMessage)
{
	uint16_t retval;
	if (pMessage == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = pMessage->wRecordsInMessage;
	}
	return retval;
}


NDEF_Message_t * NDEF_Msg_DecodeFromByteArray(const uint8_t * pByteArray, uint32_t  dwLength)
{
	void * pRecord;
	NDEF_Message_t * pMessage = NULL;
	INT_FetchedHeader_t * pNode = NULL;
	uint16_t wNumOfRecords;
	uint16_t wCounter;
	Status_t status = NDEF_STATUS_SUCCESS;
	uint8_t bErrorFlag = 0;

	RETURN_NULL_ON_NULL(pByteArray);

	pNode = Msg_FetchHeaders(pByteArray, dwLength, &wNumOfRecords);
	RETURN_NULL_ON_NULL(pNode);
	for (wCounter = 0; wCounter < wNumOfRecords; wCounter++)
	{
		pRecord = INT_ParsePayload(&pNode[wCounter], pByteArray);
		BREAK_ON_NULL(pRecord, bErrorFlag);
		status = NDEF_Msg_AppendRecord(&pMessage, pRecord);
		BREAK_ON_FAILURE(status);
	}
	if (bErrorFlag || status != NDEF_STATUS_SUCCESS)
	{
		NDEF_Msg_DestroyMsg(&pMessage);
	}
	INT_FREE(pNode);
	return pMessage;
}




uint32_t NDEF_Msg_EncodeToByteArray(NDEF_Message_t  * pMessage, uint8_t * pByteArray, uint32_t  dwLength)
{
	uint32_t dwMessageLen = 0;
	uint16_t wCounter;
	uint32_t dwRecSize;
	uint32_t dwOffset = 0;
	RecNode_t * pNode;
	Status_t status;
	NDEF_Rec_GenericCasting_t * pRec;

	if (pMessage == NULL || pByteArray == NULL)
	{
		return 0;
	}
	status = NDEF_Msg_UpdateHeaderFlags(pMessage);
	pNode = pMessage->pFirstNode;
		

	for (wCounter = 0; wCounter < pMessage->wRecordsInMessage; wCounter++)
	{
		pRec = (NDEF_Rec_GenericCasting_t*)pNode->pRecord;
		dwRecSize = INT_Header_CalcSize(pRec);
		dwRecSize += NDEF_Rec_GetPayloadLen(pRec);
		if ((uint64_t)dwOffset + dwRecSize > dwLength)
		{
			status = NDEF_STATUS_ERROR;
			break;
		}
		status = INT_SerializeRecord[pRec->INT_sRecordInfo.eId](pRec, pByteArray, &dwOffset);
		BREAK_ON_FAILURE(status);
		pNode = pNode->pNextNode;
	}
	if (status == NDEF_STATUS_SUCCESS)
	{
		dwMessageLen = dwOffset;
	}
	return dwMessageLen;
}



Status_t NDEF_Msg_DestroyRecByIndex(NDEF_Message_t  * pMessage, uint16_t wIndex)
{
	Status_t status = NDEF_STATUS_ERROR_INVALID_PARAM;
	RecNode_t * pNode;
	uint16_t wCounter;

	RETURN_ON_NULL_PARAM(pMessage);

	if (wIndex < pMessage->wRecordsInMessage)
	{
		pNode = pMessage->pFirstNode;
		for (wCounter = 0; wCounter < wIndex; wCounter++)
		{
			pNode = pNode->pNextNode;
		}
		if (pNode->pNextNode != NULL)
		{
			pNode->pNextNode->pPrevNode = pNode->pPrevNode;
		}
		if (pNode->pPrevNode != NULL)
		{
			pNode->pPrevNode->pNextNode = pNode->pNextNode;
		}
		/* If its first node, update reference in msg structure*/
		if (wIndex == 0)
		{
			pMessage->pFirstNode = pNode->pNextNode;
		}
		/* If its last node, update reference in msg structure*/
		if (wIndex == pMessage->wRecordsInMessage - 1)
		{
			pMessage->pLastNode = pNode->pPrevNode;
		}
		status = NDEF_Rec_Destroy(&(pNode->pRecord));
		pMessage->wRecordsInMessage--;
		INT_FREE(pNode);

	}
	return status;
}

Status_t NDEF_Msg_DestroyRecByHandle(NDEF_Message_t  * pMessage, void* pRecord)
{
	Status_t status = NDEF_STATUS_ERROR;
	RecNode_t * pNode;
	uint16_t wCounter;

	RETURN_ON_NULL_PARAM(pMessage);
	RETURN_ON_NULL_PARAM(pRecord);
	pNode = pMessage->pFirstNode;
	for (wCounter = 0; wCounter < pMessage->wRecordsInMessage; wCounter++)
	{
		if (pRecord == pNode->pRecord)
		{
			if (pNode->pNextNode != NULL)
			{
				pNode->pNextNode->pPrevNode = pNode->pPrevNode;
			}
			if (pNode->pPrevNode != NULL)
			{
				pNode->pPrevNode->pNextNode = pNode->pNextNode;
			}
			/* If its first node, update reference in msg structure*/
			if (wCounter == 0)
			{
				pMessage->pFirstNode = pNode->pNextNode;
			}
			/* If its last node, update reference in msg structure*/
			if (wCounter == pMessage->wRecordsInMessage - 1)
			{
				pMessage->pLastNode = pNode->pPrevNode;
			}
			status = NDEF_Rec_Destroy(&(pNode->pRecord));
			pMessage->wRecordsInMessage--;
			INT_FREE(pNode);
			break;
		}
		pNode = pNode->pNextNode;
	}


	return status;
}


Status_t NDEF_Msg_DestroyMsg(NDEF_Message_t  ** ppMessage)
{
	NDEF_Message_t * pMessage;
	RETURN_ON_NULL_PARAM(ppMessage);
	RETURN_ON_NULL_PARAM(*ppMessage);

	pMessage = *ppMessage;
	while (pMessage->wRecordsInMessage != 0)
	{
		NDEF_Msg_DestroyRecByIndex(pMessage, 0);
	}
	INT_FREE(*ppMessage);
	*ppMessage = NULL;
	return NDEF_STATUS_SUCCESS;

}


NDEF_Message_t * NDEF_Msg_CreateSimpleHandover(void * pCarrierRecord)
{
	/* Phase 2*/
	return 0;
}


/* ********************************************************************************************
* Private Functions
* ******************************************************************************************* */
NDEF_Message_t *  NDEF_Msg_Create(const void * pRecord)
{
	NDEF_Message_t * pMessage;
	RecNode_t * pNode;

	pMessage = INT_MALLOC(sizeof(NDEF_Message_t));
	RETURN_NULL_ON_NULL(pMessage);
	memset(pMessage, 0, sizeof(NDEF_Message_t));
	pNode = INT_MALLOC(sizeof(RecNode_t));
	pMessage->pFirstNode = INT_MALLOC(sizeof(RecNode_t));
	pMessage->pLastNode = pMessage->pFirstNode;
	RETURN_NULL_ON_NULL(pMessage->pFirstNode);
	pMessage->pFirstNode->pPrevNode = NULL;
	pMessage->pFirstNode->pNextNode = NULL;
	pMessage->pFirstNode->pRecord = (void *)pRecord;
	return pMessage;

}

/* Used to set MB ME and SR flags according to placement of records in message and payload length */
Status_t NDEF_Msg_UpdateHeaderFlags(NDEF_Message_t * pMessage)
{
	uint8_t bFlags;
	uint8_t bFlagsTemp;
	uint16_t wCounter;
	uint32_t dwPayloadLen;
	RecNode_t * pNode;
	Status_t status = NDEF_STATUS_ERROR;

	pNode = pMessage->pFirstNode;
	for (wCounter = 0; wCounter < pMessage->wRecordsInMessage; wCounter++)
	{

		bFlags = 0;
		/* Set MB flag into first record*/
		if (wCounter == 0)
		{
			bFlags |= NDEF_HEADER_FLAG_MB;
		}


		/* Set ME flag into last record*/
		if (wCounter == pMessage->wRecordsInMessage - 1)
		{
			bFlags |= NDEF_HEADER_FLAG_ME;
		}

		/* Set SR flag into records with payload  under 255 bytes  */
		dwPayloadLen = NDEF_Rec_GetPayloadLen(pNode->pRecord);
		if (dwPayloadLen <= NDEF_HEADER_SR_PAYLOAD_THRESHOLD)
		{
			bFlags |= NDEF_HEADER_FLAG_SR;
		}
		bFlagsTemp = NDEF_Rec_GetFlags(pNode->pRecord);
		/* clear required flags*/
		bFlagsTemp &= 0x28u;
		bFlags |= bFlagsTemp;
		status = NDEF_Rec_SetFlags(pNode->pRecord, bFlags);
		BREAK_ON_FAILURE(status);
		pNode = pNode->pNextNode;
	}
	return status;
}

/* ********************************************************************************************
* Internal functions - not part of API
* ******************************************************************************************* */
Status_t INT_UninitRecHook(void *pRecord, uint8_t * pOutput, uint32_t * pOffset)
{
	return NDEF_STATUS_ERROR;
}

void INT_FreeFetchedHeaders(INT_FetchedHeader_t * pNode, uint16_t wNumOfRecords)
{
	uint16_t wCounter;
	for (wCounter = 0; wCounter < wNumOfRecords; wCounter++)
	{
		INT_FREE(pNode[wCounter].sHeader.pRecordId);
		pNode[wCounter].sHeader.pRecordId = NULL;
		INT_FREE(pNode[wCounter].sHeader.pRecordType);
		pNode[wCounter].sHeader.pRecordType = NULL;

	}
	INT_FREE(pNode);
}


INT_FetchedHeader_t * Msg_FetchHeaders(const uint8_t * pInput, uint32_t dwLength, uint16_t * pNumOfRecords)
{
	INT_FetchedHeader_t * pNode = NULL;
	void * pTempPtr;
	uint32_t dwOffset = 0;
	uint32_t dwPayloadLen;
	uint16_t wNodeIndex = 0;
	uint16_t wNumOfRecords = 0;
	uint8_t bFlagsTnf;
	uint8_t bErrorFlag = 0;
	uint8_t bIdLen;
	uint8_t bTypeLen;
	NDEF_Record_Header_Tnf_t eTnf;
	/* The first byte of serialized ndef message should have MB set to 1 , indicating the begining of the message*/
	if ((pInput[dwOffset] & NDEF_HEADER_FLAG_MB) == NDEF_HEADER_FLAG_MB)
	{
		do
		{
			pTempPtr = INT_REALLOC(pNode, sizeof(INT_FetchedHeader_t)*(wNumOfRecords + 1));
			BREAK_ON_NULL(pTempPtr, bErrorFlag);
			pNode = pTempPtr;
			memset(&pNode[wNodeIndex], 0, sizeof(INT_FetchedHeader_t));

			/* Number of records needs to be incremented right after REALLOC error code check to allow proper functionality of Msg_FreeFetchedHeaders*/
			wNumOfRecords++;

			bIdLen = 0;
			bTypeLen = 0;

			/* Fetch Flags and Tnf value*/
			CHECK_OFFSET_VALIDITY(NDEF_HEADER_FLAGS_TNF_LEN);
			bFlagsTnf = pInput[dwOffset++];
			pNode[wNodeIndex].sHeader.bFlagsTnf = bFlagsTnf;
			eTnf = ((NDEF_Record_Header_Tnf_t)(bFlagsTnf & NDEF_HEADER_TNF_MASK));
			if (eTnf<EmptyRecord&&eTnf>UnchangedRecord)
			{
				bErrorFlag = ERROR_OCURRED;
				break;
			}
			/* Empty record should not contain TypeLen IdLen, PayloadLen , Type field, Id field nor Payload field*/
			if (eTnf == EmptyRecord)
			{
				pNode[wNodeIndex].dwOffsetToPayload = 0;
				pNode[wNodeIndex].sHeader.bIdLength = 0;
				pNode[wNodeIndex].sHeader.bTypeLength = 0;
				pNode[wNodeIndex].sHeader.dwPayloadLength = 0;
				pNode[wNodeIndex].sHeader.pRecordId = NULL;
				pNode[wNodeIndex].sHeader.pRecordType = NULL;
			}
			else
			{

				/*Fetch Type Length */
				CHECK_OFFSET_VALIDITY(NDEF_HEADER_TYPELEN_LEN);
				bTypeLen = pInput[dwOffset++];
				pNode[wNodeIndex].sHeader.bTypeLength = bTypeLen;



				/*  Add  Payload len into offset calculation variable -for both short and normal records */
				if ((bFlagsTnf & NDEF_HEADER_FLAG_SR) == NDEF_HEADER_FLAG_SR)
				{
					CHECK_OFFSET_VALIDITY(NDEF_HEADER_SR_PAYLOAD_LEN);
					dwPayloadLen = (uint32_t)(pInput[dwOffset++]);
					pNode[wNodeIndex].sHeader.dwPayloadLength = dwPayloadLen;
				}
				else
				{
					CHECK_OFFSET_VALIDITY(NDEF_HEADER_PAYLOAD_LEN);
					dwPayloadLen = 0;
					dwPayloadLen |= ((uint32_t)pInput[dwOffset++]) << 24;
					dwPayloadLen |= ((uint32_t)pInput[dwOffset++]) << 16;
					dwPayloadLen |= ((uint32_t)pInput[dwOffset++]) << 8;
					dwPayloadLen |= ((uint32_t)pInput[dwOffset++]);
					pNode[wNodeIndex].sHeader.dwPayloadLength = dwPayloadLen;
				}
				if ((bFlagsTnf & NDEF_HEADER_FLAG_IL) == NDEF_HEADER_FLAG_IL)
				{
					/* Fetch ID len and ID field*/
					CHECK_OFFSET_VALIDITY(NDEF_HEADER_IDLEN_LEN);
					bIdLen = pInput[dwOffset++];
					pNode[wNodeIndex].sHeader.bIdLength = bIdLen;
					pNode[wNodeIndex].sHeader.pRecordId = INT_MALLOC(sizeof(uint8_t)* bIdLen);
					BREAK_ON_NULL(pNode[wNodeIndex].sHeader.pRecordId, bErrorFlag);
					CHECK_OFFSET_VALIDITY(bTypeLen + bIdLen);
					memcpy(pNode[wNodeIndex].sHeader.pRecordId, &pInput[dwOffset + bTypeLen], bIdLen);
				}
				else
				{
					pNode[wNodeIndex].sHeader.bIdLength = 0;
					pNode[wNodeIndex].sHeader.pRecordId = NULL;
				}

				/*Fetch Type  if the record is not of the Unchanged type (middle and terminating chunks) */
				if (eTnf != UnchangedRecord)
				{
					CHECK_OFFSET_VALIDITY(bTypeLen);
					pNode[wNodeIndex].sHeader.pRecordType = INT_MALLOC(sizeof(uint8_t)* bTypeLen);
					BREAK_ON_NULL(pNode[wNodeIndex].sHeader.pRecordType, bErrorFlag);
					memcpy(pNode[wNodeIndex].sHeader.pRecordType, &pInput[dwOffset], bTypeLen);
				}

				dwOffset = dwOffset + ((uint32_t)bTypeLen) + ((uint32_t)bIdLen);

				CHECK_OFFSET_VALIDITY(dwPayloadLen);
				/* Save offset to payload into fetch struct*/
				pNode[wNodeIndex].dwOffsetToPayload = dwOffset;
				/* Calculate offset to another record */
				dwOffset += dwPayloadLen;
			}


			wNodeIndex++;

			/*Boundary check before jumping to another record */
			if (dwOffset > dwLength)
			{
				bErrorFlag = ERROR_OCURRED;
				break;
			}

		} while ((bFlagsTnf & NDEF_HEADER_FLAG_ME) != NDEF_HEADER_FLAG_ME);
		if (bErrorFlag)
		{
			INT_FreeFetchedHeaders(pNode, wNumOfRecords);
			pNode = NULL;
		}
		*pNumOfRecords = wNumOfRecords;
	}
	return pNode;
}