#include "Platform.h"

#ifndef UNDER_CE

#include <stdlib.h>
#include <stdio.h>
#include "SerialCommunication.h"
#include "CCIDReader.h"


#define base CBaseCommunication

#define IOCTL_CCID_INT        0x80722216L
#define IOCTL_CCID_ABORT      0x80722218L
#define IOCTL_CCID_DESCRIPTOR 0x8072221CL
#define IOCTL_CCID_DRIVERVERSION 0x8072221EL




CSerialCommunication::CSerialCommunication(char *cDeviceName,CReader *Owner)
                  :CBaseCommunication(cDeviceName,Owner)
{
	m_hDevice=INVALID_HANDLE_VALUE;
	m_hAck=CreateEvent(NULL,FALSE,FALSE,NULL);
	m_hReadBufferSig=CreateEvent(NULL,FALSE,FALSE,NULL);
	m_pReadBuffer=rbuffer;
	m_ReadBufferSize=sizeof(rbuffer);
	m_PollThread=NULL;



	memset(&m_olTransmit,0,sizeof(m_olTransmit));
	m_olTransmit.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
	memset(&m_olAck,0,sizeof(m_olAck));
	m_olAck.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
	memset(&m_r_over,0,sizeof(m_r_over));
	m_r_over.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);

}

int CSerialCommunication::Open()
{
	DWORD PollThreadId;
   m_hDevice=CreateFile(m_cDeviceName,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,NULL);
	if(m_hDevice!=INVALID_HANDLE_VALUE)
	{
      DCB dcb;
      COMMTIMEOUTS timeout;
	   memset(&dcb,0,sizeof(dcb));
	   dcb.DCBlength=sizeof(dcb);

	   timeout.ReadIntervalTimeout=0;
	   timeout.ReadTotalTimeoutMultiplier=0; 
	   timeout.ReadTotalTimeoutConstant=0; 

	   timeout.WriteTotalTimeoutMultiplier=1000; 
	   timeout.WriteTotalTimeoutConstant=1000; 
	   dcb.fBinary=TRUE;
	   dcb.fParity=FALSE;
	   dcb.Parity=NOPARITY;
	   dcb.StopBits=ONESTOPBIT;
	   dcb.ByteSize=8;
	   dcb.BaudRate=CBR_115200;
	   dcb.fOutxCtsFlow=FALSE;
	   dcb.fOutxDsrFlow=FALSE;
	   dcb.fDtrControl=DTR_CONTROL_DISABLE;
	   dcb.fDsrSensitivity=FALSE;
	   dcb.fRtsControl=RTS_CONTROL_DISABLE;
	   dcb.wReserved=0;
	   dcb.fOutX=FALSE;
	   dcb.fInX=FALSE;
	   dcb.fErrorChar=FALSE;
	   dcb.fNull=FALSE;
	   dcb.fAbortOnError=FALSE;


		SetupComm(m_hDevice,1026,1026);
	   SetCommState(m_hDevice,&dcb);
	   SetCommTimeouts(m_hDevice,&timeout);
	   SetCommMask(m_hDevice,EV_CTS/* | EV_TXEMPTY*/);
      SetCommBreak(m_hDevice);
		Sleep(1);
      ClearCommBreak(m_hDevice);
      PurgeComm(m_hDevice,PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_RXABORT);
		Sleep(10);
		if(m_Reader)
		{
			if(m_Reader->GetEnviroment("SerialDoPoll",1))
			{
				m_PollThread=CreateThread(NULL,0,(  DWORD (WINAPI *)( LPVOID ))_SerialPollThread,this,0,&PollThreadId);
			}
		}
	   StartInterruptPipe();
		return 1;
	}
	return 0;
}

void CSerialCommunication::Close(void)
{
	m_CritClose.Enter();
   _Close();
	if(m_PollThread)
	{
		WaitForSingleObject(m_PollThread,2000);
			Sleep(50);
		TerminateThread(m_PollThread,0);
		CloseHandle(m_PollThread);
		m_PollThread=NULL;
	}
	HaltInterruptPipe();
	m_CritClose.Leave();
}

void CSerialCommunication::_Close(void)
{
	SetEvent(m_hReadBufferSig);
	SetEvent(m_hAck);
	m_CritClose.Enter();
	if(m_hDevice!=INVALID_HANDLE_VALUE)
	{
		HANDLE merk=m_hDevice;
		m_hDevice=INVALID_HANDLE_VALUE;
		CloseHandle(merk);
	}
	m_CritClose.Leave();
}


void CSerialCommunication::SetCommunicationString(cj_ReaderInfo *ReaderInfo)
{
	ReaderInfo->PID=0x0400;

	ReaderInfo->PortID=atoi(m_cDeviceName+strlen(m_cDeviceName)-2);
	if(ReaderInfo->PortID==0)
	   ReaderInfo->PortID=atoi(m_cDeviceName+strlen(m_cDeviceName)-1);
	memcpy(ReaderInfo->CommunicationString,"COM",4);

	ReaderInfo->ContentsMask=	RSCT_READER_MASK_PID |
										RSCT_READER_MASK_COM_TYPE |
										RSCT_READER_MASK_PORT_ID;
}



CSerialCommunication::~CSerialCommunication(void)
{
	Close();

	if(m_r_over.hEvent)
		CloseHandle(m_r_over.hEvent);
	if(m_olAck.hEvent)
		CloseHandle(m_olAck.hEvent);
	if(m_olTransmit.hEvent)
		CloseHandle(m_olTransmit.hEvent);
	CloseHandle(m_hAck);
	CloseHandle(m_hReadBufferSig);

}

int CSerialCommunication::WriteAck(uint8_t Ack)
{
   DWORD nRet;
	if(!IsConnected())
		return CJ_ERR_DEVICE_LOST;

	ResetEvent(m_olAck.hEvent);
	nRet=0;
	m_olAck.Internal=0;
	m_olAck.InternalHigh=0;
	m_olAck.Offset=0;
	m_olAck.OffsetHigh=0;
	WriteFile(m_hDevice,&Ack,1,&nRet,&m_olAck);
	return CJ_SUCCESS;
}


int CSerialCommunication::Write(void *Message,uint32_t len)
{
   DWORD nRet;
	uint32_t i;
	unsigned char buffer[2];
	unsigned char add,xor;
	uint8_t *ptr;
	if(!IsConnected())
		return CJ_ERR_DEVICE_LOST;

	if(base::Write(Message,len)==CJ_SUCCESS)
	{
		ptr=(uint8_t *)Message;
		add=0;
		xor=0;
		for(i=0;i<len;i++)
		{
			add+=ptr[i];
			xor^=ptr[i];
		}
		buffer[0]=add;
		buffer[1]=xor;
		for(;IsConnected();)
		{
			ResetEvent(m_olTransmit.hEvent);
			nRet=0;
			m_olTransmit.Internal=0;
			m_olTransmit.InternalHigh=0;
			m_olTransmit.Offset=0;
			m_olTransmit.OffsetHigh=0;
			if(WriteFile(m_hDevice,ptr,len,&nRet,&m_olTransmit)==0)
			{
				if(GetLastError()==ERROR_IO_PENDING)
				{
					if(WaitForSingleObject(m_olTransmit.hEvent,30000)==WAIT_OBJECT_0)
						nRet=len;
					else
						nRet=0;
				}
			}
			if(nRet!=len || !IsConnected())
				break;
			ResetEvent(m_hAck);
			ResetEvent(m_olTransmit.hEvent);
			nRet=0;
			m_olTransmit.Internal=0;
			m_olTransmit.InternalHigh=0;
			m_olTransmit.Offset=0;
			m_olTransmit.OffsetHigh=0;
			if(WriteFile(m_hDevice,buffer,2,&nRet,&m_olTransmit)==0)
			{
				if(GetLastError()==ERROR_IO_PENDING)
				{
					if(WaitForSingleObject(m_olTransmit.hEvent,5000)==WAIT_OBJECT_0)
						nRet=2;
					else
						nRet=0;
				}
			}
			if(nRet!=2 || !IsConnected())
				break;
			if(WaitForSingleObject(m_hAck,10000)==WAIT_OBJECT_0)
			{
				if(m_AckByte==0xff)
					break;
			}
		}
	}
	if(m_hDevice==INVALID_HANDLE_VALUE)
      return CJ_ERR_DEVICE_LOST;   
	return CJ_SUCCESS;
}

int CSerialCommunication::Read(void *Response,uint32_t *ResponseLen)
{
	int Result;
   if(m_hDevice)
	{
		m_CritReadBuffer.Enter();
		if(m_pReadBuffer==NULL)
		{
			if(*ResponseLen>=m_ReadBufferSize)
			{
				memcpy(Response,rbuffer,m_ReadBufferSize);
				*ResponseLen=m_ReadBufferSize;
				Result=CJ_SUCCESS;
			}
			else
				Result=CJ_ERR_RBUFFER_TO_SMALL;
			m_pReadBuffer=rbuffer;
			m_ReadBufferSize=sizeof(rbuffer);
			ResetEvent(m_hReadBufferSig);
		}
		else
		{
			m_pReadBuffer=(uint8_t *)Response;
			m_ReadBufferSize=*ResponseLen;
			m_CritReadBuffer.Leave();
			WaitForSingleObject(m_hReadBufferSig,INFINITE);
			m_CritReadBuffer.Enter();
			*ResponseLen=m_ReadBufferSize;
			m_pReadBuffer=rbuffer;
			m_ReadBufferSize=sizeof(rbuffer);
			ResetEvent(m_hReadBufferSig);
			Result=CJ_SUCCESS;
		}
		m_CritReadBuffer.Leave();
	}
	else
		Result=CJ_ERR_DEVICE_LOST;
	return Result;

		
}

DWORD __stdcall CSerialCommunication::_ReadThreadRoutine(LPVOID ptr)
{
   return ((CSerialCommunication *)ptr)->ReadThreadRoutine();
}



DWORD CSerialCommunication::ReadThreadRoutine(void)
{
	uint8_t buffer[1024+2];
	DWORD nRet;


	while(m_InterruptPipeState!=HaltRequest && IsConnected())
	{
		while(m_InterruptPipeState!=HaltRequest && IsConnected())
		{
			nRet=0;
			ResetEvent(m_r_over.hEvent);
			m_r_over.Offset=0;
			m_r_over.OffsetHigh=0;
			Debug.Out(m_cDeviceName,DEBUG_MASK_COMMUNICATION_IN,"Serial -- Start:",0,0);
			if(ReadFile(m_hDevice,buffer,1,&nRet,&m_r_over)==0)
			{
				if(GetLastError()==ERROR_IO_PENDING)
				{
					if(WaitForSingleObject(m_r_over.hEvent,INFINITE)==WAIT_OBJECT_0)
						nRet=1;
					else
						nRet=0;
				}
			}
			if(nRet!=1 || !IsConnected())
				break;
			if(buffer[0]==RDR_TO_PC_NOTIFYSLOTCHANGE || buffer[0]==RDR_TO_PC_HARWAREERROR || buffer[0]==RDR_TO_PC_KEYEVENT)
			{
				nRet=0;
				ResetEvent(m_r_over.hEvent);
				m_r_over.Offset=0;
				m_r_over.OffsetHigh=0;
				if(ReadFile(m_hDevice,buffer+1,3,&nRet,&m_r_over)==0)
				{
					if(GetLastError()==ERROR_IO_PENDING)
					{
						if(WaitForSingleObject(m_r_over.hEvent,5000)==WAIT_OBJECT_0)
							nRet=3;
						else
							nRet=0;
					}
				}
				if(nRet!=3 || !IsConnected())
					break;
				if(buffer[0]+buffer[1]==buffer[2] && (buffer[0]^buffer[1])==buffer[3] && m_Reader)
					m_Reader->DoInterruptCallback(buffer,(uint32_t)2);
			}
			else if(buffer[0]==0xff || buffer[0]==0x00)
			{
				m_AckByte=buffer[0];
				SetEvent(m_hAck);
			}
			else
			{
				DWORD toRead;
				unsigned char add;
				unsigned char xor;
				nRet=0;
				ResetEvent(m_r_over.hEvent);
				m_r_over.Offset=0;
				m_r_over.OffsetHigh=0;
				if(ReadFile(m_hDevice,buffer+1,9,&nRet,&m_r_over)==0)
				{
					if(GetLastError()==ERROR_IO_PENDING)
					{
						if(WaitForSingleObject(m_r_over.hEvent,10000)==WAIT_OBJECT_0)
							nRet=9;
						else
							nRet=0;
					}
				}
				if(nRet!=9 || !IsConnected())
					break;
				if(buffer[3] || buffer[4])
					break;
				toRead=2+buffer[1]+((DWORD)buffer[2]<<8);
				if(toRead>1016)

				nRet=0;
				ResetEvent(m_r_over.hEvent);
				m_r_over.Offset=0;
				m_r_over.OffsetHigh=0;
				if(ReadFile(m_hDevice,buffer+10,toRead,&nRet,&m_r_over)==0)
				{
					if(GetLastError()==ERROR_IO_PENDING)
					{
						if(WaitForSingleObject(m_r_over.hEvent,10000)==WAIT_OBJECT_0)
							nRet=toRead;
						else
							nRet=0;
					}
				}
				if(nRet!=toRead || !IsConnected())
					break;
				toRead+=8;
				xor=0;
				add=0;
				for(nRet=0;nRet<toRead;nRet++)
				{
					xor^=buffer[nRet];
					add+=buffer[nRet];
				}
				if(add==buffer[toRead] && xor==buffer[toRead+1])
				{
					WriteAck(0xff);
					m_CritReadBuffer.Enter();
					if(m_pReadBuffer!=NULL && m_ReadBufferSize>=toRead)
					{
						memcpy(m_pReadBuffer,buffer,toRead);
						m_ReadBufferSize=toRead;
						m_pReadBuffer=NULL;
						SetEvent(m_hReadBufferSig);
					}
					m_CritReadBuffer.Leave();
				}
				else
					WriteAck(0);
			}
		}
	}
	return CJ_SUCCESS;
}


int CSerialCommunication::StartInterruptPipe()
{
	DWORD ThreadId;
	m_InterruptPipeState=Running;
   m_ReadPipeThread=CreateThread(NULL,0,(  DWORD (WINAPI *)( LPVOID ))_ReadThreadRoutine,this,0,&ThreadId);
	return 0;
}

int CSerialCommunication::HaltInterruptPipe()
{
	if(m_InterruptPipeState!=UnInit)
	{
		m_InterruptPipeState=HaltRequest;
		if(m_r_over.hEvent)
		   SetEvent(m_r_over.hEvent);
		WaitForSingleObject(m_ReadPipeThread,2000);
			Sleep(50);
		TerminateThread(m_ReadPipeThread,0);
		CloseHandle(m_ReadPipeThread);
		m_InterruptPipeState=UnInit;
	}
	return 0;
}

CBaseReader *CSerialCommunication::BuildReaderObject()
{
   DWORD PollThreadId;
	m_Reader = new CECAReader(m_Owner,this);
	if(m_Reader->GetEnviroment("SerialDoPoll",1))
	{
		m_PollThread=CreateThread(NULL,0,(  DWORD (WINAPI *)( LPVOID ))_SerialPollThread,this,0,&PollThreadId);
	}
	return m_Reader;
}

bool CSerialCommunication::IsConnected()
{
	return (m_hDevice!=INVALID_HANDLE_VALUE);
}


DWORD __stdcall CSerialCommunication::_SerialPollThread(LPVOID ptr)
{
   return ((CSerialCommunication *)ptr)->SerialPollThreadRoutine();
}

DWORD CSerialCommunication::SerialPollThreadRoutine(void)
{
	DWORD Status;
	DWORD Rts=SETRTS;
	int i;
	OVERLAPPED over;

	i=1;
	memset(&over,0,sizeof(over));
	over.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
	if(GetCommModemStatus(m_hDevice,&Status))
	{
		if((Status&MS_CTS_ON)==MS_CTS_ON)
			Rts=CLRRTS;
		for(;IsConnected();)
		{
			if(!WaitCommEvent(m_hDevice,&Status,&over))
			{
				if(GetLastError()!=ERROR_IO_PENDING)
				{
					_Close();
				}
				else
				{
					while(WaitForSingleObject(over.hEvent,100)==WAIT_TIMEOUT && IsConnected())
					{
						if(i==0)
						{
   						_Close();
							break;
						}
						else if(i>0)
						{
							i--;
							if(i==0)
            				EscapeCommFunction(m_hDevice,Rts);
						}
					}
					if(!IsConnected())
						continue;
					if(Status & EV_CTS)
					{
						if(Rts==SETRTS)
							Rts=CLRRTS;
						else
							Rts=SETRTS;
						i=5;
					}
					if(Status & EV_TXEMPTY)
					{
//						SetEvent(tx_empty_event);
					}
				}
			}
			else
			{
				if(Status & EV_CTS)
				{
					if(Rts==SETRTS)
						Rts=CLRRTS;
					else
						Rts=SETRTS;
					i=5;
				}
				if(Status & EV_TXEMPTY)
				{
//					SetEvent(tx_empty_event);
				}
			}
		}
	}
	else
	{
		_Close();
	}
//   SetEvent(o_receive.hEvent);
//	CancelIo(m_hDevice);
	CloseHandle(over.hEvent);
	return (DWORD)CJ_ERR_DEVICE_LOST;
}

#endif // #ifdef UNDER_CE
