#include "stdafx.h"
#include "HIDProtocol.h"

#include "Utility.h"

enum ECARDTYPE
{
	ECARD_ISO14443A		= 0x00,
	ECARD_ISO14443B,
	ECARD_ICODE_UID,
	ECARD_ICODE_EPC,
	ECARD_ICODE,
	ECARD_SR176,
	ECARD_ISO15693,
	ECARD_UNKNOWN		= 0xFF,
};

typedef struct _CAEN_CMD 
{
	BYTE*	pCmd;
	BYTE	bCmdData;
}CAEN_CMD, *PCAEN_CMD;

CAEN_CMD tbCmd[] =
{
	{(BYTE*)"v",	0x01}, // get reader version 
	{(BYTE*)"rp",	0x02}, // read eeprom
	{(BYTE*)"o",	0x01}, // set protocol
	{(BYTE*)".",	0x01}, // abort cont read
	{(BYTE*)"m",	0x01}, // get cards / select cards
	{(BYTE*)"l",	0x01},	// auth mifire
	{(BYTE*)"r",	0x01},	// read block
	{(BYTE*)"w",	0x01},	// read block
};

CHIDProtocol::CHIDProtocol()
{

}

CHIDProtocol::~CHIDProtocol()
{

}

EERROR CHIDProtocol::SendCmd(ERF_CMD eCmd, BYTE* pData, DWORD dwData)
{
	// total Len = 3 bytes RFE + cmd +  2 bytes len + data + 2 bytes check sum
	DWORD dwCmdLen = tbCmd[eCmd].bCmdData + dwData;

	BYTE* pDataSend = new BYTE[dwCmdLen];
	ZeroMemory(pDataSend, dwCmdLen);

	memcpy_s(pDataSend, dwCmdLen, tbCmd[eCmd].pCmd, tbCmd[eCmd].bCmdData);
	memcpy_s(pDataSend + tbCmd[eCmd].bCmdData, dwCmdLen, pData, dwData);		

	EERROR eRet = m_clsTransport.Write(pDataSend, dwCmdLen);
	delete[] pDataSend;

	return eRet;
}

EERROR CHIDProtocol::RecvCmd(BYTE* pData, DWORD& dwData)
{
	BYTE pDataRet[MAX_PATH]; DWORD dwDataRet = 0x01, dwNav = 0x00;
	BOOL bStop = FALSE;

	EERROR eRet = ER_INVALID_DATA;
	ZeroMemory(pDataRet, MAX_PATH);
	while((bStop == FALSE) && (m_clsTransport.Read(pDataRet + dwNav, dwDataRet) == ER_OK))
	{
		if((strstr((char*)pDataRet, "\x0D\x0A") != NULL) || (strstr((char*)pDataRet, "\x0D\x0A") != NULL))
		{
			bStop = TRUE;
			eRet = ER_OK;
		}

		dwNav++;
		dwDataRet = 0x01;
	}

	if(eRet == ER_OK)
	{
		eRet = CUtility::SetDataByte(pDataRet, dwNav - 0x02, pData, dwData);
	}

	return eRet;
}

EERROR CHIDProtocol::Open()
{
	EERROR eRet = m_clsTransport.Open();
	if(eRet == ER_OK)
	{
		Sleep(100);
		BYTE pDataRet[0x10]; DWORD dwDataRet = 0x10;
		if((eRet = m_clsTransport.Read(pDataRet, dwDataRet)) == ER_OK)
		{
			// aboard continues read if we are in continues read
			AboartContRead();
		}
	}
	return eRet;
}

EERROR CHIDProtocol::AboartContRead()
{
	EERROR eRet = SendCmd(RF_ABORT_CONT_READ, NULL, 0x00);
	if(eRet == ER_OK)
	{
		BYTE pDataRet[MAX_PATH]; DWORD dwDataRet = MAX_PATH, dwNav = 0x00;
		BOOL bStop = FALSE;

		eRet = ER_INVALID_DATA;
		ZeroMemory(pDataRet, MAX_PATH);
		while((bStop == FALSE) && (RecvCmd(pDataRet, dwDataRet) == ER_OK))
		{
			if(dwDataRet == 0x01 && (pDataRet[0x00] == 'S' || pDataRet[0x00] == '?'))
			{
				bStop = TRUE;
				eRet = ER_OK;
			}
			ZeroMemory(pDataRet, MAX_PATH);
			dwDataRet = MAX_PATH;
		}
	}
	return eRet;
}

EERROR CHIDProtocol::GetVersion(BYTE* pData, DWORD& dwSize)
{
	EERROR eRet = SendCmd(RF_VERSION, NULL, 0x00);
	if(eRet == ER_OK)
	{
		eRet = RecvCmd(pData, dwSize);
	}
	return eRet;
}

EERROR CHIDProtocol::GetStationID(BYTE* pData, DWORD& dwSize)
{
	EERROR eRet = SendCmd(RF_READ_EEPROM, (BYTE*)"0A", 0x02);
	if(eRet == ER_OK)
	{
		eRet = RecvCmd(pData, dwSize);
	}
	return eRet;
}

char tbProtocol[] = {'a', 'b', 'd', 'e', 'i', 's', 'v'};
char* tbProtocolRet[] = {"OA", "OB", "OD", "OE", "OI", "OS", "OV"};
EERROR CHIDProtocol::SetProtocol(BYTE bProt)
{
	EERROR eRet =  ER_NOT_IMPLEMENTED;
	if(bProt <= ECARD_ISO15693)
	{
		eRet = SendCmd(RF_SET_PROTOCOL, (BYTE*)&tbProtocol[bProt], 0x01);
		if(eRet == ER_OK)
		{
			BYTE pDataRet[0x04]; DWORD dwDataRet = 0x04;
			ZeroMemory(pDataRet, 0x04);
			if((eRet = RecvCmd(pDataRet, dwDataRet)) == ER_OK)
			{
				eRet = ER_NO_PROTOCOL;
				if(memcmp(tbProtocolRet[bProt], pDataRet, 0x02) == 0x00)
				{
					eRet		= ER_OK;
					m_bSelProt	= bProt;
				}
			}
		}
	}
	return eRet;
}

EERROR CHIDProtocol::GetCard(BYTE* pData, DWORD dwSize, DWORD& dwNrCards)
{
	EERROR eRet = ER_INVALID_DATA;
	dwNrCards	= 0x00;
	if(pData != NULL)
	{
		eRet = ER_NOT_IMPLEMENTED;

		eRet = SendCmd(RF_GET_SEL_CARD, (BYTE*)"\x0D", 0x01);
		if(eRet == ER_OK)
		{
			BOOL bStop = FALSE;
			BYTE pDataRet[MAX_PATH]; DWORD dwDataRet = MAX_PATH;
			ZeroMemory(pDataRet, MAX_PATH);
			while((bStop == FALSE) && ((eRet = RecvCmd(pDataRet, dwDataRet)) == ER_OK))
			{
				if(dwDataRet == 0x02)
				{
					bStop = TRUE;
				}

				if(dwDataRet > 0x02)
				{
					// check if we have enough place for currect card
					DATA_STRUCT* stData = (DATA_STRUCT*)pData;
					if(((dwNrCards + 0x01) * sizeof(stData->stCardInfo)) <= dwSize)
					{
						// (stData->stCardInfo + dwNav)
						stData = (DATA_STRUCT*)(pData + dwNrCards * sizeof(stData->stCardInfo));

						BYTE* pCardId = NULL; DWORD dwCardSize = 0x00;
						CUtility::HexToByte(pDataRet, dwDataRet, &pCardId, dwCardSize);
						memcpy_s(stData->stCardInfo.pCardId, 0x20, pCardId, dwCardSize);
						stData->stCardInfo.nLength = (BYTE)dwCardSize;

						dwNrCards++;

						delete[] pCardId;
					}
				}

				ZeroMemory(pDataRet, MAX_PATH);
				dwDataRet = MAX_PATH;
			}
		}
	}
	return eRet;
}

EERROR CHIDProtocol::SelectCard()
{
	std::string strCardId = CUtility::DataToString(m_stReadWriteInfo.pCardId, m_stReadWriteInfo.bCardIdLen, FALSE);
	std::string strSelCardId = strCardId; strSelCardId += "\x0D";

	EERROR eRet = SendCmd(RF_GET_SEL_CARD, (BYTE*)strSelCardId.c_str(), strSelCardId.size());
	if(eRet == ER_OK)
	{
		BYTE pDataRet[MAX_PATH]; DWORD dwData = MAX_PATH;
		ZeroMemory(pDataRet, MAX_PATH);

		if((eRet = RecvCmd(pDataRet, dwData)) == ER_OK)
		{
			eRet = ER_NO_TAG;
			if(strCardId == (CHAR*)pDataRet)
			{
				eRet = ER_OK;
			}
		}
	}
	return eRet;
}

void CHIDProtocol::SetReadWriteInfo(READ_WRITE_INFO* pReadWrite)
{
	if(pReadWrite != NULL)
	{
		m_stReadWriteInfo = *pReadWrite;
	}
}

EERROR CHIDProtocol::AuthIso14443A(UINT nSect)
{
	BYTE pDataRet[0x10]; DWORD dwDataRet = 0x10;
	ZeroMemory(pDataRet, 0x10);
	pDataRet[0x00] = (BYTE)nSect;

	std::string strSend = CUtility::DataToString(pDataRet, 0x01, FALSE);
	strSend += m_stReadWriteInfo.bKeyB ? "BB" : "AA";
	strSend += CUtility::DataToString(m_stReadWriteInfo.pKey, 0x06, FALSE);
	strSend += "\x0D";

	EERROR eRet = SendCmd(RF_AUTH_14443A, (BYTE*)strSend.c_str(), strSend.size());
	if(eRet == ER_OK)
	{
		ZeroMemory(pDataRet, 0x10);
		if((eRet = RecvCmd(pDataRet, dwDataRet)) == ER_OK)
		{
			eRet = ER_NAK;
			if(pDataRet[0x00] == 'L')
			{
				eRet = ER_OK;
			}
		}
	}
	return eRet;
}

EERROR CHIDProtocol::Read(BYTE* pData, DWORD& dwData)
{
	EERROR eRet = ER_INVALID_DATA;
	if(pData != NULL)
	{
		if((eRet = SelectCard()) == ER_OK)
		{
			UINT nNrSect = 0x00; DWORD dwDataNav = 0x00;
			while((eRet == ER_OK) && (nNrSect < m_stReadWriteInfo.ulNrSect))
			{
				if(m_bSelProt == ECARD_ISO14443A)
				{
					eRet = AuthIso14443A(m_stReadWriteInfo.ulStartSect + nNrSect);
				}

				if(eRet == ER_OK)
				{
					BYTE pDataRet[MAX_PATH]; DWORD dwDataRet = MAX_PATH;
					ZeroMemory(pDataRet, 0x10);
					pDataRet[0x00] = (BYTE)(m_stReadWriteInfo.ulStartSect + nNrSect);

					std::string strSend = CUtility::DataToString(pDataRet, 0x01, FALSE);
					strSend += "\x0D";

					eRet = SendCmd(RF_READ, (BYTE*)strSend.c_str(), strSend.size());
					if(eRet == ER_OK)
					{
						ZeroMemory(pDataRet, MAX_PATH);
						if((eRet = RecvCmd(pDataRet, dwDataRet)) == ER_OK)
						{
							eRet = ER_READ;
							// no error detected then read data
							if(dwDataRet > 0x02)
							{
								BYTE* pDataSect = NULL; DWORD dwDataSect = 0x00, dwDataRest = 0x00;
								CUtility::HexToByte(pDataRet, dwDataRet, &pDataSect, dwDataSect);

								dwDataRest = dwData - dwDataNav;
								eRet = CUtility::SetDataByte(pDataSect, dwDataSect, pData + dwDataNav, dwDataRest);
								dwDataNav += dwDataRest;

								delete[] pDataSect;
							}
						}
					}
				}
				nNrSect++;
			}
			dwData = dwDataNav;
		}
	}

	return eRet;
}

EERROR CHIDProtocol::Write(BYTE* pData, DWORD dwData)
{
	EERROR eRet = ER_INVALID_DATA;
	if(pData != NULL)
	{
		if((eRet = SelectCard()) == ER_OK)
		{
			UINT nNrSect = 0x00; DWORD dwDataNav = 0x00; UINT nSectSize = dwData / m_stReadWriteInfo.ulNrSect;
			while((eRet == ER_OK) && (nNrSect < m_stReadWriteInfo.ulNrSect))
			{
				if(m_bSelProt == ECARD_ISO14443A)
				{
					eRet = AuthIso14443A(m_stReadWriteInfo.ulStartSect + nNrSect);
				}

				if(eRet == ER_OK)
				{
					BYTE pDataRet[MAX_PATH]; DWORD dwDataRet = MAX_PATH;
					ZeroMemory(pDataRet, 0x10);
					pDataRet[0x00] = (BYTE)(m_stReadWriteInfo.ulStartSect + nNrSect);

					std::string strSend = CUtility::DataToString(pDataRet, 0x01, FALSE);
					strSend += CUtility::DataToString(pData + dwDataNav, nSectSize, FALSE);
					strSend += "\x0D";

					eRet = SendCmd(RF_WRITE, (BYTE*)strSend.c_str(), strSend.size());
					if(eRet == ER_OK)
					{
						ZeroMemory(pDataRet, MAX_PATH);
						if((eRet = RecvCmd(pDataRet, dwDataRet)) == ER_OK)
						{
							eRet = ER_READ;
							// no error detected then read data
							strSend = CUtility::DataToString(pData + dwDataNav, nSectSize, FALSE);
							if(strSend == (CHAR*)pDataRet)
							{
								eRet = ER_OK;
								dwDataNav += nSectSize;
							}
						}
					}
				}
				nNrSect++;
			}
			dwData = dwDataNav;
		}
	}

	return eRet;
}

EERROR CHIDProtocol::Close()
{
	return m_clsTransport.Close();
}