#include <stdio.h>
#ifndef TARGET_OSX
#include "config.h"
#endif
#include "MCU_ATR.h"
#include "ATR.h"

#define MCU_ATR_DEFAULT_F	372
#define MCU_ATR_DEFAULT_D	1
#define MCU_ATR_DEFAULT_I 	50
#define MCU_ATR_DEFAULT_N	0
#define MCU_ATR_DEFAULT_P	5

static unsigned long
FSDecode[16] = {4000000L, 5000000L, 6000000L, 8000000L,
				12000000L, 16000000L, 20000000L, 0,
				0, 5000000L, 7500000L, 10000000L,
				15000000L, 20000000L, 0, 0};

static int
TDItoNumInterface[16] = {	0, 1, 1, 2,
							1, 2, 2, 3,
							1, 2, 2, 3,
							2, 3, 3, 4};

int
MCU_ATR_FIDecode[16] = {372, 372, 558, 744,
						1116, 1488, 1860, 0,
						0, 512, 768, 1024,
						1536, 2048, 0, 0};

double
MCU_ATR_DIDecode[16] = {0, 1, 2, 4,
						8, 16, 32, 0,
						12, 20, 0.5, 0.25,
						125, 0.0625, 0.03125, 0.015625};

int
MCU_ATR_IIDecode[4] = {25, 50, 100, 0};

MCU_ATR_RESULT
MCUAtrInit(	PMCU_ATR	pMCUAtr,
			PUCHAR		atrBuf,
			int			atrBufLen)
{
	UCHAR			buffer[MCU_ATR_MAX_SIZE];
	UCHAR			TDi;
	int				protocolNo;
	int				bufPtr;
	MCU_ATR_RESULT	result;

	/* Check size of buffer */
	if (atrBufLen < 2)
	{
		printf(	"MCUAtrInit: atrBufLen(%d) < 2\n",
				atrBufLen);
		result = MCU_ATR_MALFORMED;
		goto ATRBufLenTooSmall;
	}

	/* Check if ATR is from a inverse convention card */
	if (atrBuf[0] == (UCHAR)~(INVERT_BYTE(ATR_TS_CONVENTION_INVERSE)))
	{
		for (bufPtr = 0; bufPtr < atrBufLen; bufPtr++)
		{
			buffer[bufPtr] = ~(INVERT_BYTE(atrBuf[bufPtr]));
		}
	}
	else
	{
		memcpy(buffer, atrBuf, atrBufLen);
	}

	/* Store T0 and TS */
	pMCUAtr->TS	= buffer[0];
	pMCUAtr->T0 = TDi = buffer[1];

	/* Store number of historical bytes */
	pMCUAtr->historicalSize = ATR_TDI_DATA(TDi);

	/* TCK is not present by default */
	(pMCUAtr->TCK).bPresent	= FALSE;

	/* Extract interface bytes */
	protocolNo	= 0;
	bufPtr		= 1;
	while (bufPtr < atrBufLen)
	{
		/* Check buffer is long enought */
		if (bufPtr + TDItoNumInterface[ATR_TDI_INTERFACE(TDi)] >=
			atrBufLen)
		{
			printf(	"MCUAtrInit: [%s:%d] TDi(0x%X) bufPtr(%d) "
					"atrBufLen(%d) incorrect\n",
					__FILE__,
					__LINE__,
					TDi,
					bufPtr,
					atrBufLen);
			result = MCU_ATR_MALFORMED;
			goto TDiIncorrect;
		}

		if (protocolNo >= MCU_ATR_MAX_PROTOCOLS)
		{
			printf(	"MCUAtrInit: protocolNo(%d) >= "
					"MCU_ATR_MAX_PROTOCOLS(%d)\n",
					protocolNo,
					MCU_ATR_MAX_PROTOCOLS);
			result = MCU_ATR_MALFORMED;
			goto TooManyProtocol;
		}

		if (TDi & ATR_TDI_TA_MASK)
		{	/* TAi is present */
			bufPtr++;
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TA].value =
				buffer[bufPtr];
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TA].bPresent =
				TRUE;
		}
		else
		{
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TA].bPresent =
				FALSE;
		}

		if (TDi & ATR_TDI_TB_MASK)
		{	/* TBi is present */
			bufPtr++;
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TB].value =
				buffer[bufPtr];
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TB].bPresent =
				TRUE;
		}
		else
		{
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TB].bPresent =
				FALSE;
		}

		if (TDi & ATR_TDI_TC_MASK)
		{	/* TCi is present */
			bufPtr++;
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TC].value =
				buffer[bufPtr];
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TC].bPresent =
				TRUE;
		}
		else
		{
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TC].bPresent =
				FALSE;
		}

		if (TDi & ATR_TDI_TD_MASK)
		{	/* TDi is present */
			bufPtr++;
			TDi = pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TD].value =
				buffer[bufPtr];
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TD].bPresent =
				TRUE;

			(pMCUAtr->TCK).bPresent =
				(ATR_TDI_DATA(TDi) != ATR_TDI_PROTO_T0);
			protocolNo++;
		}
		else
		{
			pMCUAtr->ib[protocolNo][MCU_ATR_INTERFACE_TC].bPresent =
				FALSE;
			break;
		}
	}
	pMCUAtr->numProtocol = protocolNo + 1;

	/* Store Historical Data */
	if ((bufPtr + pMCUAtr->historicalSize) >= atrBufLen)
	{
		printf(	"MCUAtrInit: bufPtr(%d) + historicalSize(%d) "
				">= atrBufLen(%d)\n",
				bufPtr,
				pMCUAtr->historicalSize,
				atrBufLen);
		result = MCU_ATR_MALFORMED;
		goto StoreHistoricalDataFailed;
	}
	memcpy(	pMCUAtr->historical,
			&buffer[bufPtr + 1],
			pMCUAtr->historicalSize);
	bufPtr += pMCUAtr->historicalSize;

	/* Store TCK */
	if ((pMCUAtr->TCK).bPresent)
	{
		if ((bufPtr + 1) >= atrBufLen)
		{
			printf(	"MCUAtrInit: [TCK] bufPtr(%d) + 1 >= "
					"atrBufLen(%d)\n",
					bufPtr,
					atrBufLen);
			result = MCU_ATR_MALFORMED;
			goto StoreTCKFailed;
		}
		bufPtr++;
		(pMCUAtr->TCK).value = buffer[bufPtr];
	}
	pMCUAtr->length = bufPtr + 1;
	return MCU_ATR_OK;
	
/****** Error Part ******/
StoreTCKFailed:
StoreHistoricalDataFailed:
TooManyProtocol:
TDiIncorrect:
ATRBufLenTooSmall:
	return result;
}

void
MCUAtrCleanUp(PMCU_ATR	pMCUAtr)
{
}

MCU_ATR_RESULT
MCUAtrGetConvention(PMCU_ATR			pMCUAtr,
					PMCU_ATR_CONVENTION	pConvention)
{
	if (pMCUAtr->TS == ATR_TS_CONVENTION_DIRECT)
	{
		*pConvention = MCU_ATR_CONVENTION_DIRECT;
		return MCU_ATR_OK;
	}
	else if (pMCUAtr->TS == ATR_TS_CONVENTION_INVERSE)
	{
		*pConvention = MCU_ATR_CONVENTION_INVERSE;
		return MCU_ATR_OK;
	}
	else
	{
		return MCU_ATR_MALFORMED;
	}
}

int
MCUAtrGetNumProtocol(PMCU_ATR	pMCUAtr)
{
	return pMCUAtr->numProtocol;
}

MCU_ATR_RESULT
MCUAtrGetProtocol(	PMCU_ATR		pMCUAtr,
					int				protoNo,
					PMCU_ATR_PROTO	pProto)
{
	if ((protoNo < 2) ||
		(protoNo > pMCUAtr->numProtocol))
	{
		return MCU_ATR_NOT_FOUND;
	}

	if (pMCUAtr->ib[protoNo - 2][MCU_ATR_INTERFACE_TD].bPresent)
	{
		*pProto = ATR_TDI_DATA(
				pMCUAtr->ib[protoNo - 2][MCU_ATR_INTERFACE_TD].value);
	}
	else
	{
		*pProto = MCU_ATR_PROTO_T0;
	}
	return MCU_ATR_OK;
}

MCU_ATR_RESULT
MCUAtrGetInterfaceByte(	PMCU_ATR			pMCUAtr,
						int					protoNo,
						MCU_ATR_INTERFACE	interfaceNo,
						PUCHAR				pValue)
{
	if ((protoNo < 1) ||
		(protoNo > pMCUAtr->numProtocol))
	{
		return MCU_ATR_NOT_FOUND;
	}

	if (pMCUAtr->ib[protoNo - 1][interfaceNo].bPresent)
	{
		*pValue = pMCUAtr->ib[protoNo - 1][interfaceNo].value;
		return MCU_ATR_OK;
	}
	else
	{
		return MCU_ATR_NOT_FOUND;
	}
}

MCU_ATR_RESULT
MCUAtrGetIntegerValue(	PMCU_ATR				pMCUAtr,
						MCU_ATR_INTEGER_VALUE	integerValue,
						PUCHAR					pValue)
{
	MCU_ATR_RESULT	result;

	switch (integerValue)
	{
	case MCU_ATR_INTEGER_VALUE_FI:
		if (pMCUAtr->ib[0][MCU_ATR_INTERFACE_TA].bPresent)
		{
			*pValue = ATR_TA1_FI(
						pMCUAtr->ib[0][MCU_ATR_INTERFACE_TA].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	case MCU_ATR_INTEGER_VALUE_DI:
		if (pMCUAtr->ib[0][MCU_ATR_INTERFACE_TA].bPresent)
		{
			*pValue = ATR_TA1_DI(
						pMCUAtr->ib[0][MCU_ATR_INTERFACE_TA].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	case MCU_ATR_INTEGER_VALUE_II:
		if (pMCUAtr->ib[0][MCU_ATR_INTERFACE_TB].bPresent)
		{
			*pValue = ATR_TB1_II(
						pMCUAtr->ib[0][MCU_ATR_INTERFACE_TB].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	case MCU_ATR_INTEGER_VALUE_PI1:
		if (pMCUAtr->ib[0][MCU_ATR_INTERFACE_TB].bPresent)
		{
			*pValue = ATR_TB1_PI1(
						pMCUAtr->ib[0][MCU_ATR_INTERFACE_TB].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	case MCU_ATR_INTEGER_VALUE_N:
		if (pMCUAtr->ib[0][MCU_ATR_INTERFACE_TC].bPresent)
		{
			*pValue = ATR_TC1_N(
						pMCUAtr->ib[0][MCU_ATR_INTERFACE_TC].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	case MCU_ATR_INTEGER_VALUE_PI2:
		if (pMCUAtr->ib[1][MCU_ATR_INTERFACE_TB].bPresent)
		{
			*pValue = ATR_TB2_PI2(
						pMCUAtr->ib[1][MCU_ATR_INTERFACE_TB].value);
			result = MCU_ATR_OK;
		}
		else
		{
			result = MCU_ATR_NOT_FOUND;
		}
		break;

	default:
		result = MCU_ATR_NOT_FOUND;
	}
	return result;
}

double
MCUAtrGetParameter(	PMCU_ATR			pMCUAtr,
					MCU_ATR_PARAMETER	parameter)
{
	UCHAR	integerValue;
	double	result;

	switch (parameter)
	{
	case MCU_ATR_PARAMETER_F:
		if (MCUAtrGetIntegerValue(	pMCUAtr,
									MCU_ATR_INTEGER_VALUE_FI,
									&integerValue) == MCU_ATR_OK)
		{
			result = (double)MCU_ATR_FIDecode[integerValue];
		}
		else
		{
			result = (double)MCU_ATR_DEFAULT_F;
		}
		break;

	case MCU_ATR_PARAMETER_D:
		if (MCUAtrGetIntegerValue(	pMCUAtr,
									MCU_ATR_INTEGER_VALUE_DI,
									&integerValue) == MCU_ATR_OK)
		{
			result = (double)MCU_ATR_DIDecode[integerValue];
		}
		else
		{
			result = (double)MCU_ATR_DEFAULT_D;
		}
		break;

	case MCU_ATR_PARAMETER_I:
		if (MCUAtrGetIntegerValue(	pMCUAtr,
									MCU_ATR_INTEGER_VALUE_II,
									&integerValue) == MCU_ATR_OK)
		{
			result = (double)MCU_ATR_IIDecode[integerValue];
		}
		else
		{
			result = (double)MCU_ATR_DEFAULT_I;
		}
		break;

	case MCU_ATR_PARAMETER_P:
		if (MCUAtrGetIntegerValue(	pMCUAtr,
									MCU_ATR_INTEGER_VALUE_PI2,
									&integerValue) == MCU_ATR_OK)
		{
			result = (double)integerValue;
		}
		else if (MCUAtrGetIntegerValue(	pMCUAtr,
										MCU_ATR_INTEGER_VALUE_PI1,
										&integerValue) == MCU_ATR_OK)
		{
			result = (double)integerValue;
		}
		else
		{
			result = (double)MCU_ATR_DEFAULT_P;
		}
		break;

	case MCU_ATR_PARAMETER_N:
		if (MCUAtrGetIntegerValue(	pMCUAtr,
									MCU_ATR_INTEGER_VALUE_N,
									&integerValue) == MCU_ATR_OK)
		{
			result = (double)integerValue;
		}
		else
		{
			result = (double)MCU_ATR_DEFAULT_N;
		}
		break;

	default:
		result = 0.0;
	}
	return result;
}

MCU_ATR_RESULT
MCUAtrGetHistoricalBytes(	PMCU_ATR	pMCUAtr,
							PUCHAR		pHist,
							int*		pLength)
{
	if (pMCUAtr->historicalSize == 0)
	{
		return MCU_ATR_NOT_FOUND;
	}
	else
	{
		*pLength = pMCUAtr->historicalSize;
		memcpy(pHist, pMCUAtr->historical, pMCUAtr->historicalSize);
		return MCU_ATR_OK;
	}
}

MCU_ATR_RESULT
MCUAtrGetCheckByte(	PMCU_ATR	pMCUAtr,
					PUCHAR		pCheckByte)
{
	if (pMCUAtr->TCK.bPresent)
	{
		*pCheckByte = pMCUAtr->TCK.value;
		return MCU_ATR_OK;
	}
	else
	{
		return MCU_ATR_NOT_FOUND;
	}
}

unsigned long
MCUAtrGetFsMax(PMCU_ATR	pMCUAtr)
{
	UCHAR	integerValue;

	if (MCUAtrGetIntegerValue(	pMCUAtr,
								MCU_ATR_INTEGER_VALUE_FI,
								&integerValue) == MCU_ATR_OK)
	{
		return FSDecode[integerValue];
	}
	else
	{
		return FSDecode[1];
	}
}

double
MCUAtrDecodeFI(UCHAR	fi)
{
	return (double)MCU_ATR_FIDecode[fi];
}

double
MCUAtrDecodeDI(UCHAR	di)
{
	return (double)MCU_ATR_DIDecode[di];
}
