#include "Platform.h"

#ifdef UNDER_CE


#include <stdlib.h>
#include <stdio.h>
#include "USBCommunicationCe.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


#define CJECA_CE_DRIVER_INFO_SIZE	2048

CUSBCommunicationCe::CUSBCommunicationCe(const char *cDeviceName,CReader *Owner)
                    :CBaseCommunication(cDeviceName,Owner)
{
	m_pSBuffer=NULL;
	m_nSBufferLen=0;
	m_hDevice=INVALID_HANDLE_VALUE;

	m_pDriverInfo=(USB_DEVICE_CE *)new int8_t [CJECA_CE_DRIVER_INFO_SIZE];
	memset (m_pDriverInfo,0x00,CJECA_CE_DRIVER_INFO_SIZE);
}

int CUSBCommunicationCe::Open()
{

	m_hDevice=CreateFile (L"CJU1:",		
						  GENERIC_READ | GENERIC_WRITE,
						  0,
						  NULL,
						  OPEN_EXISTING,
						  0,
						  NULL);

	if(m_hDevice!=INVALID_HANDLE_VALUE)
	{
	 //TODO  StartInterruptPipe();
		return 1;
	}
	return 0;
}
 
void CUSBCommunicationCe::Close(void)
{
	m_CritClose.Enter();
   _Close();
	HaltInterruptPipe();
	m_CritClose.Leave();
}

void CUSBCommunicationCe::_Close(void)
{
	m_CritClose.Enter();
	if(m_hDevice!=INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hDevice);
		m_hDevice=INVALID_HANDLE_VALUE;
	}
	m_CritClose.Leave();
}


void CUSBCommunicationCe::SetCommunicationString(cj_ReaderInfo *ReaderInfo)
{
//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString() - Start",L"cjeca32.dll",MB_ICONSTOP);
	int8_t *ptr;
	ReaderInfo->PID=m_pDriverInfo->ProductId;
	ReaderInfo->PortID=atoi(m_cDeviceName+strlen(m_cDeviceName)-2);

//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString(1)",L"cjeca32.dll",MB_ICONSTOP);
	memcpy(ReaderInfo->CommunicationString,"USB",4);
	ptr=((int8_t *)m_pDriverInfo)+m_pDriverInfo->ProductOffs;

//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString(2a)",L"cjeca32.dll",MB_ICONSTOP);
	
	

	WCHAR wzBuffer[128]=L"";

	wsprintf (wzBuffer,L"ProdLen(1): %d bytes",m_pDriverInfo->ProductLength);
//	::MessageBox (0,wzBuffer,L"cjeca32.dll",MB_ICONSTOP);
//	::MessageBox (0,ptr,"cjeca32.dll",MB_ICONSTOP);

	memcpy(ReaderInfo->ProductString,ptr,m_pDriverInfo->ProductLength);

//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString(2b)",L"cjeca32.dll",MB_ICONSTOP);
	ptr=((int8_t *)m_pDriverInfo)+m_pDriverInfo->ManufacturerOffs;


//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString(3)",L"cjeca32.dll",MB_ICONSTOP);
	memcpy(ReaderInfo->VendorString,ptr,m_pDriverInfo->ManufacturerLength);

	ReaderInfo->ContentsMask=	RSCT_READER_MASK_PID |
										RSCT_READER_MASK_VENDOR_STRING |
										RSCT_READER_MASK_PRODUCT_STRING |
										RSCT_READER_MASK_COM_TYPE |
										RSCT_READER_MASK_PORT_ID;


//	::MessageBox (0,L"CUSBCommunicationCe::SetCommunicationString() - Stop",L"cjeca32.dll",MB_ICONSTOP);
}



CUSBCommunicationCe::~CUSBCommunicationCe(void)
{
	Close();
	if(m_pSBuffer)
		delete m_pSBuffer;
	delete m_pDriverInfo;

}

int CUSBCommunicationCe::Write(void *Message,uint32_t len)
{
	DWORD nRet;

	if(base::Write(Message,len)==CJ_SUCCESS)
	{
		if(len+4 > m_nSBufferLen)
		{
			if(m_pSBuffer)
				delete m_pSBuffer;

			m_pSBuffer= new uint8_t[len+4];
			m_nSBufferLen=len+4;
		}

		for(;;)
		{
			nRet=0;
			memcpy(m_pSBuffer,Message,len);

			if(!WriteFile(m_hDevice,m_pSBuffer,(DWORD)len,&nRet,0))
			{
				DWORD nLastErr=::GetLastError ();
				return CJ_ERR_DEVICE_LOST;
			}
			
			break;

	   }
	};
		   
	if(m_hDevice==INVALID_HANDLE_VALUE)
		return CJ_ERR_DEVICE_LOST;   
	
	return CJ_SUCCESS;
}

int CUSBCommunicationCe::Read(void *Response,uint32_t *ResponseLen)
{
	DWORD		nRetBytes;

//	::MessageBox (0,L"CUSBCommunicationCe::Read ()",L"cjeca32.dll",MB_ICONSTOP);

	if(m_hDevice==0)
	{
//		::MessageBox (0,L"CUSBCommunicationCe::Read (0) -> CJ_ERR_DEVICE_LOST",L"cjeca32.dll",MB_ICONSTOP);
		return CJ_ERR_DEVICE_LOST;
	};
	
	nRetBytes=0;
			
	if(!ReadFile(m_hDevice,Response,(DWORD)*ResponseLen,&nRetBytes,0))
	{
//		::MessageBox (0,L"CUSBCommunicationCe::Read (1) -> CJ_ERR_DEVICE_LOST",L"cjeca32.dll",MB_ICONSTOP);
		return CJ_ERR_DEVICE_LOST;
	};
	
	CBaseCommunication::Read(Response,(uint32_t *)&nRetBytes);
	*ResponseLen=(int)nRetBytes;

	WCHAR wzBuffer[100]=L"";

	wsprintf (wzBuffer,L"Bytes read: %d bytes",nRetBytes);
//	::MessageBox (0,wzBuffer,L"cjeca32.dll",MB_ICONSTOP);

	return CJ_SUCCESS;
}

DWORD __stdcall CUSBCommunicationCe::_IntPipeThreadRoutine(LPVOID ptr)
{
   return ((CUSBCommunicationCe *)ptr)->IntPipeThreadRoutine();
}


DWORD CUSBCommunicationCe::IntPipeThreadRoutine()
{
   uint8_t buffer[270];
   OVERLAPPED over;
   DWORD nRet;
   DWORD last;


   memset(&over,0,sizeof(over));
   over.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
   while(m_InterruptPipeState!=HaltRequest && m_hDevice)
   {
      nRet=0;
      ResetEvent(over.hEvent);
      over.Offset=0;
      over.OffsetHigh=0;
      memcpy(buffer,"CJCC",4);
		Debug.Out(m_cDeviceName,DEBUG_MASK_COMMUNICATION_IN,"CCID INT -- Start:",0,0);
		if(!DeviceIoControl(m_hDevice,IOCTL_CCID_INT,NULL,0,buffer,270,&nRet,&over))
		{
			if((last=GetLastError())!=ERROR_IO_PENDING)
			{
			  if(last==ERROR_INVALID_HANDLE)
			  {
				  _Close();
				  return CJ_ERR_DEVICE_LOST;
			  }
			  else
				  Sleep(100);
			}
//			if(!GetOverlappedResult(m_hDevice,&over,&nRet,TRUE))
			{
				if(GetLastError()==ERROR_INVALID_HANDLE)
				{
					_Close();
					return CJ_ERR_DEVICE_LOST;
				}
				else
					Sleep(100);
			}
		}
		if(nRet)
		{
			if(m_Reader)
		      m_Reader->DoInterruptCallback(buffer,(uint32_t)nRet);
		}
   }
   CloseHandle(over.hEvent);
   return CJ_SUCCESS;
}


int CUSBCommunicationCe::StartInterruptPipe()
{
	DWORD ThreadId;
	m_InterruptPipeState=Running;
   m_IntPipeThread=CreateThread(NULL,0,(  DWORD (WINAPI *)( LPVOID ))_IntPipeThreadRoutine,this,0,&ThreadId);
	return 0;
}

int CUSBCommunicationCe::HaltInterruptPipe()
{
	if(m_InterruptPipeState!=UnInit)
	{
		m_InterruptPipeState=HaltRequest;
		WaitForSingleObject(m_IntPipeThread,2000);
			Sleep(50);
		TerminateThread(m_IntPipeThread,0);
		CloseHandle(m_IntPipeThread);
		m_InterruptPipeState=UnInit;
	}
	return 0;
}

CBaseReader *CUSBCommunicationCe::BuildReaderObject()
{
	m_Reader = new CECAReader(m_Owner,this);
	return m_Reader;
	int8_t *ptr;


	DWORD nRet=0;

//	::MessageBox (0,L"CBaseReader::BuildReaderObject(1)",L"cjeca32.dll",MB_ICONSTOP);

	if(!DeviceIoControl(m_hDevice,IOCTL_CCID_DESCRIPTOR,m_pDriverInfo,2048,m_pDriverInfo,2048,&nRet,0))
	{
		ULONG nErr=::GetLastError ();
		Close();
		return NULL;
	}

//	::MessageBox (0,L"CBaseReader::BuildReaderObject(2)",L"cjeca32.dll",MB_ICONSTOP);

	if(nRet<sizeof(USB_DEVICE_CE))
	{
		m_Owner->DebugLeveled(DEBUG_MASK_COMMUNICATION_ERROR,"Unknown SYS - driver: struct error");
		Close();
		return NULL;
	}


	if(m_pDriverInfo->VendorId!=0x0c4b)
	{
		m_Owner->DebugLeveled(DEBUG_MASK_COMMUNICATION_ERROR,"Unknown device vendor");
		Close();
		return NULL;
	}

	ptr=((int8_t *)m_pDriverInfo)+m_pDriverInfo->ProductOffs;

	switch(m_pDriverInfo->ProductId)
	{
	case 0x0400:
		if(memcmp(ptr,"cyberJack e-com(a)",m_pDriverInfo->ProductLength)==0)
			m_Reader = new CECAReader(m_Owner,this);
		else
			m_Reader = new CSECReader(m_Owner,this);
		return m_Reader;
	default:
		m_Owner->DebugLeveled(DEBUG_MASK_COMMUNICATION_ERROR,"Unknown device product");
		return NULL;
	}
	

}

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

#endif // #ifdef UNDER_CE
