#include "Reader.h"

//varity
long m_bProtocolType;							//PC/SC Reader protocol
SCARDCONTEXT DeviceContext;						//PC/SC Reader device context
unsigned int m_bReaderCounter;					//Reader counter
unsigned int m_bActiveNO;						//current actived reader
BOOL m_bActiveFlag;								//IS Reader connected?
char ReaderName[NUMBER_OF_READERS][NAME_LENGTH];//saved Reader name buffer

BYTE SendAPDU[MAX_APDU_BUFFER];					//send APDU Buffer
BYTE ReceiveAPDU[MAX_APDU_BUFFER];				//receive APDU Buffer
SCARDHANDLE hScardHandle;						//Reader handle

//Updata reader counter
//Return : reader counter
int UpdataReader(void)
{
	// TODO: Add your control notification handler code here
	long lReturn;

	m_bActiveFlag = FALSE;
	m_bActiveNO = 0;
	m_bReaderCounter = 0;
	
	//Is connect
	if(m_bActiveFlag)
	{
		//Connected
		SCardDisconnect(hScardHandle,SCARD_EJECT_CARD);
		SCardReleaseContext(DeviceContext);
	}
 
	//release device context
	lReturn = SCardEstablishContext(SCARD_SCOPE_USER,NULL,NULL,&DeviceContext);
	if(SCARD_S_SUCCESS == lReturn)
	{
		//Successful: establish context
		LPTSTR pmszReaders = NULL;
		LPTSTR pReader;
		DWORD  cch = SCARD_AUTOALLOCATE;
		DWORD StringLen = 0;

		lReturn = SCardListReaders(DeviceContext,NULL,(LPTSTR)&pmszReaders, &cch);
		
		if(SCARD_S_SUCCESS == lReturn)
		{
			//have reader
			pReader = pmszReaders;
			while(cch > StringLen+1)
			{
				strcpy(ReaderName[m_bReaderCounter++], (LPCTSTR)(pReader+StringLen));
				StringLen += strlen((LPCTSTR) pmszReaders+StringLen);
				StringLen += 1;
			}
			
			//release
			SCardFreeMemory(DeviceContext,pmszReaders);

			return m_bReaderCounter;
		}
	}
	
	return 0;	
}

//connect Reader
BOOL ConnectReader(unsigned int ReaderNO)
{
	// TODO: Add your control notification handler code here
	long lReturn;
	DWORD ActiveProtocol;

	//reader counter out of range
	if(m_bReaderCounter < (ReaderNO+1))
	{
		return FALSE;
	}

	if(!m_bActiveFlag)
	{
		//no connected
		SCardDisconnect(hScardHandle,SCARD_EJECT_CARD);
		m_bProtocolType = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
		lReturn = SCardConnect(DeviceContext,ReaderName[ReaderNO],SCARD_SHARE_EXCLUSIVE, m_bProtocolType, &hScardHandle, &ActiveProtocol);
		if(SCARD_S_SUCCESS == lReturn)
		{
			m_bProtocolType = ActiveProtocol;
			m_bActiveNO     = ReaderNO;
			m_bActiveFlag   = TRUE;

			return TRUE;
		}
	}
	return FALSE;	
}

//disconnect reader
BOOL DisconnectReader(void)
{
	long lReturn;

	if(m_bActiveFlag)
	{
		lReturn = SCardDisconnect(hScardHandle,SCARD_EJECT_CARD);
		if(SCARD_S_SUCCESS != lReturn)
		{
			return FALSE;	
		}
	}

	m_bActiveFlag = FALSE;	
	return TRUE;
}

//send APDU
int APDUCommand(PBYTE APDU, unsigned int iLen, PBYTE cReceiveData, unsigned int *iReceiveLen)
{
	BYTE cReceive[MAX_RESPONSE];
	long ReceiveLen;
	unsigned char SW1, SW2;
	SCARD_IO_REQUEST IO_Request;
	long lReturn;
	
	IO_Request.dwProtocol = m_bProtocolType;
	IO_Request.cbPciLength = (DWORD) sizeof(SCARD_IO_REQUEST);
	ReceiveLen = sizeof(cReceive);
	
	lReturn = SCardTransmit(hScardHandle, &IO_Request, APDU, iLen, 0, cReceive, (LPDWORD)&ReceiveLen);
	
	if(lReturn != SCARD_S_SUCCESS)
	{
		*iReceiveLen = 0;
		return 0xFFFF;
	}
	memcpy(cReceiveData, cReceive, ReceiveLen);
	*iReceiveLen = (long)ReceiveLen-2;
	SW1 = cReceive[ReceiveLen - 2];
	SW2 = cReceive[ReceiveLen - 1];
	
	return MAKEWORD(SW2,SW1);	
}

//get card info APDU
int GetData(unsigned int iCardType, unsigned char *cReceiveData, unsigned int *iReceiveLen)
{
	int iSW = ERROR_GENERAL;

	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xCA;
		SendAPDU[2] = (BYTE)(iCardType>>8);
		SendAPDU[3] = (BYTE)(iCardType);
		SendAPDU[4] = 0x00;

		iSW = APDUCommand(SendAPDU, 5, cReceiveData, iReceiveLen);
	}

	return iSW;
}

//Authentication
int Authentication(unsigned int iBlockAddr, unsigned char cKeyType, unsigned char cKeyCofig, unsigned char *cpKeyData)
{
	int iSW = ERROR_GENERAL;
	unsigned int iSendLen;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0x88;
		SendAPDU[2] = (BYTE)(iBlockAddr>>8);
		SendAPDU[3] = (BYTE)(iBlockAddr);
		SendAPDU[4] = (BYTE)cKeyType;
		SendAPDU[5] = (BYTE)cKeyCofig;

		if(cKeyCofig & 0x80 != 0)
		{
			//Authenticate used in saved key
			SendAPDU[6] = cpKeyData[0];
			iSendLen = 7;
		}
		else
		{
			//Authenticate used in current key
			iSendLen = 6+cKeyCofig;
			memcpy(&SendAPDU[6], cpKeyData, cKeyCofig);
		}

		iSW = APDUCommand(SendAPDU, iSendLen, ReceiveAPDU, &iReceiveLen);
	}
	
	return iSW;
}

//read card block
int ReadBinaryBlock(unsigned int iBlockAddr, unsigned char *cReceiveData, unsigned int *iReceiveLen)
{
	int iSW = ERROR_GENERAL;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xB0;
		SendAPDU[2] = (BYTE)(iBlockAddr>>8);
		SendAPDU[3] = (BYTE)(iBlockAddr);
		SendAPDU[4] = (BYTE)(*iReceiveLen);
		
		iSW = APDUCommand(SendAPDU, 5, ReceiveAPDU, iReceiveLen);
		if(SUCCESS == iSW)
		{
			memcpy(cReceiveData, ReceiveAPDU, *iReceiveLen);
		}
	}
	
	return iSW;	
}

//write card block
int UpdataBinaryBlock(unsigned int iBlockAddr, unsigned char *cSendData, unsigned int iDataLen)
{
	int iSW = ERROR_GENERAL;
	unsigned int iSendLen;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xD6;
		SendAPDU[2] = (BYTE)(iBlockAddr>>8);
		SendAPDU[3] = (BYTE)(iBlockAddr);
		SendAPDU[4] = (BYTE)iDataLen;
		
		memcpy(&SendAPDU[5], cSendData, iDataLen);
		iSendLen = 5+iDataLen;
		
		iSW = APDUCommand(SendAPDU, iSendLen, ReceiveAPDU, &iReceiveLen);
	}
	
	return iSW;
}

//value operation: initialize purse, increase purse, decrease purse
int ValueBlockOperation(unsigned int iBlockAddr, unsigned char cOperateType, long iValue)
{
	int iSW = ERROR_GENERAL;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xD7;
		SendAPDU[2] = (BYTE)(iBlockAddr>>8);
		SendAPDU[3] = (BYTE)(iBlockAddr);
		SendAPDU[4] = 0x05;
		SendAPDU[5] = (BYTE)cOperateType;
		SendAPDU[9] = (BYTE)(iValue>>24);
		SendAPDU[8] = (BYTE)(iValue>>16);
		SendAPDU[7] = (BYTE)(iValue>>8);
		SendAPDU[6] = (BYTE)(iValue);
		
		iSW = APDUCommand(SendAPDU, 10, ReceiveAPDU, &iReceiveLen);
	}
	
	return iSW;
}

//read purse
int ReadValueBlock(unsigned int iBlockAddr, long *iValue)
{
	int iSW = ERROR_GENERAL;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xB1;
		SendAPDU[2] = (BYTE)(iBlockAddr>>8);
		SendAPDU[3] = (BYTE)(iBlockAddr);
		SendAPDU[4] = 0x04;
		
		iSW = APDUCommand(SendAPDU, 5, ReceiveAPDU, &iReceiveLen);
		if(SUCCESS == iSW)
		{
			memcpy((unsigned char *)&iValue[0], ReceiveAPDU, 4);
		}
	}
	
	return iSW;
}

//backup purse
int RestoreValueBlock(unsigned int iSrcAddr, unsigned int iTargetAddr)
{
	int iSW = ERROR_GENERAL;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0xD7;
		SendAPDU[2] = (BYTE)(iSrcAddr>>8);
		SendAPDU[3] = (BYTE)(iSrcAddr);
		SendAPDU[4] = 0x03;
		SendAPDU[5] = 0x03;
		SendAPDU[6] = (BYTE)(iTargetAddr>>8);
		SendAPDU[7] = (BYTE)(iTargetAddr);
		
		iSW = APDUCommand(SendAPDU, 8, ReceiveAPDU, &iReceiveLen);
	}
	
	return iSW;
}

//Halt TypeA card
int TypeA_Halt(void)
{
	int iSW = ERROR_GENERAL;
	unsigned int iReceiveLen;
	
	iReceiveLen = 0;
	
	if(m_bActiveFlag)
	{
		SendAPDU[0] = 0xFF;
		SendAPDU[1] = 0x00;
		SendAPDU[2] = 0x00;
		SendAPDU[3] = 0x01;
		SendAPDU[4] = 0x00;
		
		iSW = APDUCommand(SendAPDU, 5, ReceiveAPDU, &iReceiveLen);
	}
	
	return iSW;
}