/*
*                    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_Record.h"
#include "Internal/NDEF_Heap.h"
#include "Internal/NDEF_RecordInternals.h"

/* ********************************************************************************************
*  Definitions
* ******************************************************************************************* */
#define HEADER_CLEAR_TNF(x)		(x)&=~NDEF_HEADER_TNF_MASK;
#define HEADER_CLEAR_FLAGS(x)	(x)&=~NDEF_HEADER_FLAG_MASK;
#define BTSSP_TYPE_LEN	32

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


/* ********************************************************************************************
* Private Functions Prototypes
* ******************************************************************************************* */

static void * ParserHook(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * EmptyParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * WktParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * MimeParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * AbsUriParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * ExternalParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * UnknownParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static void * UnchangedParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput);
static Status_t INT_Uninit_Free(void *pRecord);

static Status_t(*const INT_FreeRecord[TypeIdMax])(void *pRecord) =
{
	INT_Uninit_Free, INT_Generic_Free, INT_Uninit_Free, INT_Btssp_Free, INT_Btssp_Free, INT_Uninit_Free, INT_Uninit_Free, INT_Uninit_Free, INT_Text_Free, INT_Uri_Free, INT_Uninit_Free, INT_Uninit_Free, INT_Uninit_Free
};

static void * (*const PayloadParser[NDEF_HEADER_TNF_MAX_VAL])(INT_FetchedHeader_t * pFetchedHeader,const uint8_t * pInput) =
{
	EmptyParser, WktParser, MimeParser, AbsUriParser, ExternalParser, UnknownParser, UnchangedParser
};


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

Status_t NDEF_Rec_SetId(void * pRecord, const char * pId)
{
	NDEF_Rec_GenericCasting_t * rec;
	uint8_t bIdLength;
	void * pTemp;
	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(pId);
	bIdLength = strlen(pId);
	rec = (NDEF_Rec_GenericCasting_t *)pRecord;
	if (bIdLength)
	{
		pTemp = INT_REALLOC(rec->sHeader.pRecordId, bIdLength);
		RETURN_ON_NULL_MEMBER(pTemp);
		rec->sHeader.pRecordId = (uint8_t *)pTemp;
		
		rec->sHeader.bIdLength = bIdLength;
		memcpy(rec->sHeader.pRecordId, pId, rec->sHeader.bIdLength);
	}
	else
	{
		rec->sHeader.bIdLength = bIdLength;
		INT_FREE(rec->sHeader.pRecordId);
		rec->sHeader.pRecordId = NULL;
	}
	return NDEF_STATUS_SUCCESS;
}

uint8_t NDEF_Rec_GetIdLen(void * pRecord)
{
	uint8_t retval;
	if (pRecord == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = ((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bIdLength;
	}
	return retval;
}


Status_t NDEF_Rec_GetId(void * pRecord, char * pId, uint16_t wLength)
{
	NDEF_Rec_GenericCasting_t * rec;

	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(pId);
	rec = (NDEF_Rec_GenericCasting_t *)pRecord;
	RETURN_ON_NULL_MEMBER(rec->sHeader.pRecordId);
	if (rec->sHeader.bIdLength + NULL_TERM_LEN > wLength)
	{
		return NDEF_STATUS_ERROR_BUFF_OVF;
	}
	memcpy(pId, rec->sHeader.pRecordId, rec->sHeader.bIdLength);
	pId[rec->sHeader.bIdLength] = NULL_TERM;
	return NDEF_STATUS_SUCCESS;
}


Status_t NDEF_Rec_SetType(void * pRecord, const char * pType)
{
	NDEF_Rec_GenericCasting_t * rec;
	void * pTemp;
	uint8_t bTypeLength;

	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(pType);
	bTypeLength = strlen(pType);
	rec = (NDEF_Rec_GenericCasting_t *)pRecord;
	if (bTypeLength)
	{
		pTemp = INT_REALLOC(rec->sHeader.pRecordType, bTypeLength);
		RETURN_ON_NULL_MEMBER(pTemp);
		rec->sHeader.pRecordType = (uint8_t *)pTemp;


		rec->sHeader.bTypeLength = bTypeLength;
		memcpy(rec->sHeader.pRecordType, pType, bTypeLength);
	}
	else
	{
		rec->sHeader.bTypeLength = bTypeLength;
		INT_FREE(rec->sHeader.pRecordType);
		rec->sHeader.pRecordType = NULL;
	}
	return NDEF_STATUS_SUCCESS;
}

uint8_t NDEF_Rec_GetTypeLen(void * pRecord)
{
	uint8_t retval;
	if (pRecord == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = ((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bTypeLength;
	}
	return retval;
}


Status_t NDEF_Rec_GetType(void * pRecord, char * pType, uint16_t wLength)
{
	NDEF_Rec_GenericCasting_t * rec;

	RETURN_ON_NULL_PARAM(pRecord);
	RETURN_ON_NULL_PARAM(pType);
	rec = (NDEF_Rec_GenericCasting_t *)pRecord;
	RETURN_ON_NULL_MEMBER(rec->sHeader.pRecordType);
	if (rec->sHeader.bTypeLength + NULL_TERM_LEN > wLength)
	{
		return NDEF_STATUS_ERROR_BUFF_OVF;
	}
	memcpy(pType, rec->sHeader.pRecordType, rec->sHeader.bTypeLength);
	pType[rec->sHeader.bTypeLength] = NULL_TERM;
	return NDEF_STATUS_SUCCESS;
}



Status_t NDEF_Rec_SetTnf(void * pRecord, NDEF_Record_Header_Tnf_t eTnf)
{
	RETURN_ON_NULL_PARAM(pRecord);
	HEADER_CLEAR_TNF(((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf);
	((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf |= eTnf;
	return NDEF_STATUS_SUCCESS;
}

Status_t NDEF_Rec_SetFlags(void * pRecord, uint8_t bFlags)
{
	RETURN_ON_NULL_PARAM(pRecord);
	HEADER_CLEAR_FLAGS(((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf);
	((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf |= (bFlags&NDEF_HEADER_FLAG_MASK);
	return NDEF_STATUS_SUCCESS;
}


uint8_t NDEF_Rec_GetFlags(void * pRecord)
{
	uint8_t retval;
	if (pRecord == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = ((((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf)&NDEF_HEADER_FLAG_MASK);
	}
	return retval;
}


NDEF_Record_Header_Tnf_t NDEF_Rec_GetTnf(void * pRecord)
{
	uint8_t retval;
	if (pRecord == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = ((((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.bFlagsTnf)&NDEF_HEADER_TNF_MASK);
	}
	return (NDEF_Record_Header_Tnf_t)retval;
}


uint32_t NDEF_Rec_GetPayloadLen(const void * pRecord)
{
	uint32_t retval;
	if (pRecord == NULL)
	{
		retval = 0;
	}
	else
	{
		retval = ((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader.dwPayloadLength;
	}
	return retval;

}



Status_t NDEF_Rec_Destroy(void ** ppRecord)
{
	Status_t status = NDEF_STATUS_SUCCESS;
	NDEF_Rec_GenericCasting_t * pRecord;
	RETURN_ON_NULL_PARAM(ppRecord);
	pRecord = ((NDEF_Rec_GenericCasting_t *)(*ppRecord));
	INT_FreeRecord [pRecord->INT_sRecordInfo.eId](pRecord);
	INT_FREE(pRecord->sHeader.pRecordId);
	INT_FREE(pRecord->sHeader.pRecordType);
	INT_FREE(pRecord);
	*ppRecord = NULL;

	return status;
}

/* ********************************************************************************************
* Private Functions
* ******************************************************************************************* */
void * ParserHook(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	return NULL;
}

void * EmptyParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	return INT_GenPayload_Parse(pFetchedHeader, pInput);
}
void * WktParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	void * pRecord = NULL;
	uint8_t bTypeLen;
	uint8_t * pType;
	uint8_t bResult = 0;
	bTypeLen = pFetchedHeader->sHeader.bTypeLength;
	pType = pFetchedHeader->sHeader.pRecordType;
	do
	{
		if (bTypeLen == 1)
		{

			bResult = memcmp(pType, TEXT_TYPE_FIELD, 1);
			if (bResult == 0)
			{
				pRecord = INT_Text_Parse(pFetchedHeader, pInput);
				break;
			}
			bResult = memcmp(pType, URI_TYPE_FIELD, 1);
			if (bResult == 0)
			{
				pRecord = INT_Uri_Parse(pFetchedHeader, pInput);
				break;
			}
		}
		pRecord = INT_GenPayload_Parse(pFetchedHeader, pInput);
	} while (0);


	return pRecord;
}
void * MimeParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	void * pRecord = NULL;
	uint8_t bTypeLen;
	uint8_t * pType;
	uint8_t bResult = 0;
	bTypeLen = pFetchedHeader->sHeader.bTypeLength;
	pType = pFetchedHeader->sHeader.pRecordType;
	do
	{
		/* Both BLE and BREDR have the same length*/
		if (bTypeLen == BTSSP_TYPE_LEN)
		{


			bResult = memcmp(pType, BTSSP_TYPE_BREDR, BTSSP_TYPE_LEN);
			if (bResult == 0)
			{
				pRecord = INT_BtsspBrEdr_Parse(pFetchedHeader, pInput);
				break;
			}
			bResult = memcmp(pType, BTSSP_TYPE_LE, BTSSP_TYPE_LEN);
			if (bResult == 0)
			{
				pRecord = INT_BtsspLe_Parse(pFetchedHeader, pInput);
				break;
			}
		}
		pRecord = INT_GenPayload_Parse(pFetchedHeader, pInput);
	} while (0);
	return pRecord;
}
void * AbsUriParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	return INT_GenPayload_Parse(pFetchedHeader, pInput);
}
void * ExternalParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	/* For future implementations - External types needs to be compared case insensitively*/
	return INT_GenPayload_Parse(pFetchedHeader, pInput);
}
void * UnknownParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	return INT_GenPayload_Parse(pFetchedHeader, pInput);
}
void * UnchangedParser(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	return INT_GenPayload_Parse(pFetchedHeader, pInput);
}




Status_t INT_Uninit_Free(void *pRecord)
{
	/* In case some additional resources for uninitialized records needs to be freed in future, atm this fucntion does nothing  */
	return NDEF_STATUS_SUCCESS;
}

/* ********************************************************************************************
* Internal functions - not part of API
* ******************************************************************************************* */
uint32_t INT_Header_CalcSize(void *pRecord)
{
	uint32_t dwHeaderSize=0;
	NDEF_Record_Header_t * pHeader;
	uint8_t bFlags;

	pHeader = &(((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader);
	dwHeaderSize += NDEF_HEADER_FLAGS_TNF_LEN;
	bFlags = pHeader->bFlagsTnf & NDEF_HEADER_FLAG_MASK;
	if ((bFlags & NDEF_HEADER_FLAG_SR) == NDEF_HEADER_FLAG_SR)
	{
		dwHeaderSize += NDEF_HEADER_SR_PAYLOAD_LEN;
	}
	else
	{
		dwHeaderSize += NDEF_HEADER_PAYLOAD_LEN;
	}
	if (pHeader->bIdLength)
	{
		dwHeaderSize += NDEF_HEADER_IDLEN_LEN;
		dwHeaderSize += (uint32_t)pHeader->bIdLength;
	}
	if (pHeader->bTypeLength)
	{
		dwHeaderSize += NDEF_HEADER_TYPELEN_LEN;
		dwHeaderSize += (uint32_t)pHeader->bTypeLength;
	}
	return dwHeaderSize;

}


Status_t INT_Header_Serialize(void *pRecord, uint8_t * pOutput, uint32_t * pOffset)
{
	uint32_t dwOffset;
	NDEF_Record_Header_t * pHeader;

	pHeader = &(((NDEF_Rec_GenericCasting_t *)pRecord)->sHeader);
	dwOffset = *pOffset;
	pOutput[dwOffset] = pHeader->bFlagsTnf;
	dwOffset += NDEF_HEADER_FLAGS_TNF_LEN;
	if (pHeader->bTypeLength)
	{
		pOutput[dwOffset] = pHeader->bTypeLength;
		dwOffset += NDEF_HEADER_TYPELEN_LEN;
	}
	if (pHeader->dwPayloadLength < NDEF_HEADER_SR_PAYLOAD_THRESHOLD)
	{
		pOutput[dwOffset] = (uint8_t)pHeader->dwPayloadLength;
		dwOffset += NDEF_HEADER_SR_PAYLOAD_LEN;
	}
	else
	{
		/* endian independent code*/
		pOutput[dwOffset++] = (uint8_t)((pHeader->dwPayloadLength & 0xFF000000UL) >> 24);
		pOutput[dwOffset++] = (uint8_t)((pHeader->dwPayloadLength & 0x00FF0000UL) >> 16);
		pOutput[dwOffset++] = (uint8_t)((pHeader->dwPayloadLength & 0x0000FF00UL) >> 8);
		pOutput[dwOffset++] = (uint8_t)((pHeader->dwPayloadLength & 0x000000FFUL));
	}
	if (pHeader->bIdLength)
	{
		pOutput[dwOffset] = pHeader->bIdLength;
		dwOffset += NDEF_HEADER_IDLEN_LEN;
	}
	if (pHeader->bTypeLength)
	{
		memcpy(&pOutput[dwOffset], pHeader->pRecordType, pHeader->bTypeLength);
		dwOffset += pHeader->bTypeLength;
	}
	if (pHeader->bIdLength)
	{
		memcpy(&pOutput[dwOffset], pHeader->pRecordId, pHeader->bIdLength);
		dwOffset += pHeader->bIdLength;
	}
	*pOffset = dwOffset;
	return NDEF_STATUS_SUCCESS;
}


void * INT_ParsePayload(INT_FetchedHeader_t * pFetchedHeader, const uint8_t * pInput)
{
	void * pRecord = NULL;
	NDEF_Record_Header_Tnf_t eTnf;
	eTnf = (NDEF_Record_Header_Tnf_t)(pFetchedHeader->sHeader.bFlagsTnf&NDEF_HEADER_TNF_MASK);
	pRecord = PayloadParser[eTnf](pFetchedHeader, pInput);
	return pRecord;
}