/****************************************************************************************
 *
 *	TEST_EFTAPI_2.CPP
 *
 *	demonstrate the programming with EFTAPI interface of MPD
 *  event style with async mode
 *  using a thread for Completion
 *
 *  
 ****************************************************************************************/


#include <stdio.h>
#ifdef WIN32
#include <conio.h>
#include <windows.h>
#include <process.h>
#endif

#ifdef LINUX
#include <termios.h>
#include <pthread.h>
#endif

#include <string.h>
#include "eftapi.h"
#include "errors.h"

/****************************************************************************************
 *
 *	_getch and _kbhit
 *
 *	for Linux
 *
 ****************************************************************************************/

#ifdef LINUX
int _getch() { 
    static int ch = -1, fd = 0; 
    struct termios neu, alt; 
    fd = fileno(stdin); 
    tcgetattr(fd, &alt); 
    neu = alt; 
    neu.c_lflag &= ~(ICANON|ECHO); 
    tcsetattr(fd, TCSANOW, &neu); 
    ch = getchar(); 
    tcsetattr(fd, TCSANOW, &alt); 
    return ch; 
} 

int _kbhit(void) { 
        struct termios term, oterm; 
        int fd = 0; 
        int c = 0; 
        tcgetattr(fd, &oterm); 
        memcpy(&term, &oterm, sizeof(term)); 
        term.c_lflag = term.c_lflag & (!ICANON); 
        term.c_cc[VMIN] = 0; 
        term.c_cc[VTIME] = 1; 
        tcsetattr(fd, TCSANOW, &term); 
        c = getchar(); 
        tcsetattr(fd, TCSANOW, &oterm); 
        if (c != -1) 
        ungetc(c, stdin); 
        return ((c != -1) ? 1 : 0); 
} 
#endif

/****************************************************************************************
 *
 *	Global Variables
 *
 *	
 *
 ****************************************************************************************/


#ifdef WIN32
HANDLE hCompleteThread = 0;
#else
pthread_t hCompleteThread;
#endif
bool bCompleteThreadRunning = false;
EFT_HANDLE h = 0;

/****************************************************************************************
 *
 *	OnCommandComplete
 *
 *	is called, when a command is completed
 *
 ****************************************************************************************/

void OnCommandComplete(long resultCode, long complCommand)
{
	char * s;
	switch (complCommand)
	{
	case ccTransaction: s = "Transaction"; break;
	case ccCommit: s = "Commit"; break;
	case ccPartialCommit: s = "PartialCommit"; break;
	case ccOpen: s = "Open"; break;
	case ccClose: s = "Close"; break;
	case ccQueryStatus: s = "QueryStatus"; break;
	case ccBalance: s = "Balance"; break;
	case ccDeviceControl: s = "DeviceControl"; break;
	case ccIccControl: s = "ICCControl"; break;
	case ccValidation: s = "Validation"; break;
	case ccDeviceAccess: s = "DeviceAccess"; break;
	}
		

	if (resultCode == 0)
	{
		printf("...Command Completed: %d (%s)\n", complCommand, s);

		if (complCommand == ccTransaction)
		{
			/* Trx is ok -> Commit */
			printf("... Trx is ok -> Commiting\n");
			EFT_Commit(h, true);
		}
	}
	else
	{
		char msg[1000];	
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("...Command %d (%s) Completed with error %d (%s)\n", complCommand, s, resultCode, msg);
	}
}
	
/****************************************************************************************
 *
 *	OnDeviceEvent
 *
 *	is called, when a DeviceEvent is occured
 *
 ****************************************************************************************/

long readerold = -1,  deviceold = -1;
void OnDeviceEvent()
{
	long reader,  device;
	EFT_GetDeviceStatusCode(h, &device);
	if (device != deviceold)
	{
		switch (device)
		{
		case deviceClosed:
			printf("...Devicestate: closed\n");
			break;
		case deviceOpen:
			printf("...Devicestate: open\n");
			break;
		case deviceBusy:
			printf("...Devicestate: busy\n");
			break;
		case deviceNoAnswer:
			printf("...Devicestate: no answer\n");
			break;
		case deviceNotInit:
			printf("...Devicestate: not init\n");
			break;
		}
		deviceold = device;
	}

	EFT_GetDeviceEventCode(h, &reader);
	if (readerold != reader)
	{
		switch (reader)
		{
		case readerEmpty:
			printf("...Readerstate: Empty\n");
			break;
		case readerLoaded:
			printf("...Readerstate: Card Loaded\n");
			break;
		case readerEjected:
			printf("...Readerstate: Card Ejected\n");
			break;
		case readerCardRetension:
			printf("...TransactionThread: Readerstate: Card Retained\n");
			break;
		default:
			printf("...Readerstate: %d\n", reader);

		}
		readerold = reader;
	}

}

/****************************************************************************************
 *
 *	CompleteThread
 *
 *	Thread to read Complete(-1) in a endless loop
 *	Session has to be created in this Thread
 ****************************************************************************************/


#ifdef WIN32
DWORD WINAPI CompleteThread(void *pCall)
#else
void * CompleteThread(void *pCall)
#endif
{
	int r;
	long complCommand;
	long complDeviceEvent;
		

	/* start Complete Loop */
	bCompleteThreadRunning = true;
	while (bCompleteThreadRunning)
	{
		r = EFT_Complete(h, -1); /* wait until event */
		if (r == EFT_E_CLIENT_SOCKET_CLOSED)
		{
			printf ("Error: Connection to Server lost\nretry with connect, open, close or QueryStatus\n");
		}

		EFT_GetCompletedDeviceEvent(h, &complDeviceEvent);
		if (complDeviceEvent > 0)
		{
			OnDeviceEvent();
		}

		EFT_GetCompletedCommand(h, &complCommand);
		if (complCommand > 0)
		{
			OnCommandComplete(r, complCommand);
		}

	}

	return 0;
}

/****************************************************************************************
 *
 *	main
 *
 *	Starts CompleteThread
 *	and loops for input
 ****************************************************************************************/


int main()
{
	int r;
	int ch;
	long actState, readerState, deviceState, cardsRetained;
	long nCount;
	char msg[1000];

	/* set Automode */
	//EFT_PutAutoMode(h, amodeAutomat );

	/* create session and get handle */
	EFT_CreateSession(&h);
	
	/* set driver adress */
	/*
	char adr[32];
	printf("DriverAdress (0 = default = local) ? ");
	r = scanf("%s", &adr);

	if (strlen(adr) > 5)
	{
		EFT_PutDriverAddress(h, adr);
	}
	*/

	/*connect to driver */
	r = EFT_Connect(h);
	if (r != 0)
	{
		char msg[1000];	
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("EFT_Connect returned error %d: %s\n", r, msg);
		printf("End CompleteThread\n");
		return 1;
	}

	/* set asnc mode */
	EFT_PutAsync(h, 1);

#ifdef WIN32
	DWORD dwTreadId;
	if (hCompleteThread == 0){
		//Start CompleteThread
		
		hCompleteThread = CreateThread( NULL, 5000, CompleteThread,
						0,
						0,
						&dwTreadId);
	
	}
#else
	r = pthread_create( &hCompleteThread, NULL, CompleteThread, 0);
#endif


	

retryread0:
	printf(
	"MPD EFTAPI with Thread Test Programm\n"
	"o) Open\n"
	"t) Transaction\n"
	"a) Abort\n"
	"c) Close\n"
	"b) Balance\n"
	"i) Config and Init\n"
	"s) Display Device-Status\n"
	"d) Display Applications\n"
	"-) Disconnect\n"
	"0) Exit\n"
	"\n"
	);

retryread:
	printf ("\nOperation ? \n");

retryread2:
	ch = _getch();
	if (ch == 10) goto retryread2;
	printf ("\n");

	switch(ch)
	{
	case 'o': /* open */
		printf("opening EFT\n");
		r = EFT_Open(h);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Open returned error %d: %s\n", r, msg);
			break;
		}

		break;

	case 't': /* transaction async */
		printf("start transaction async\n");
		printf("to abort: press 'a'\n");
		r = EFT_Transaction(h, "debit", 150, 0);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Transaction returned error %d: %s\n", r, msg);
			break;
		}
		break;

	case 'a': /* abort transaction async */
		printf("abort transaction async\n");
		r = EFT_Abort(h);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Abort returned error %d: %s\n", r, msg);
			break;
		}
		break;


	case 'c': /* close */
		printf("closing EFT\n");
		r = EFT_Close(h);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Close returned error %d: %s\n", r, msg);
			break;
		}
		break;

	case 'b': /* balance */
		printf("balancing EFT\n");
		r = EFT_Balance(h);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Balance returned error %d: %s\n", r, msg);
			break;
		}
		break;

	case 'i': /* config and init */
		printf("balancing EFT\n");
		r = EFT_DeviceControl(h, 2 /*EFT_DCC_TERMINAL_CONFIGURATION*/, 3 /*EFT_DCA_TC_CONFIGINIT*/);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("Config and Init returned error %d: %s\n", r, msg);
			break;
		}
		break;
	case 's': /* display device status */
		/* if no connection, reconnect with QueryStatus*/
		long status;
		EFT_GetStatus(h, &status);
		if (status == 0) //IDLE
		{
			printf ("reconnect with QueryStatus, device status will be reported in CompleteThread\n");
			r = EFT_QueryStatus(h);
			break;
		}

		/* diplay device status */
		EFT_GetActivationState(h, &actState);
		switch (actState)
		{
		case shiftClosed:
			printf("Shift state: closed\n");
			break;
		case shiftOpen:
			printf("Shift state: open\n");
			break;
		}

		EFT_GetDeviceStatusCode(h, &deviceState);
		switch (deviceState)
		{
		case deviceClosed:
			printf("Devicestate: closed\n");
			break;
		case deviceOpen:
			printf("Devicestate: open\n");
			break;
		case deviceBusy:
			printf("Devicestate: busy\n");
			break;
		case deviceNoAnswer:
			printf("Devicestate: no answer\n");
			break;
		case deviceNotInit:
			printf("Devicestate: not init\n");
			break;
		}

		EFT_GetDeviceEventCode(h, &readerState);
		switch (readerState)
		{
		case readerEmpty:
			printf("Readerstate: Empty\n");
			break;
		case readerLoaded:
			printf("Readerstate: Card Loaded\n");
			break;
		case readerEjected:
			printf("Readerstate: Card Ejected\n");
			break;
		case readerCardRetension:
			printf("...TransactionThread: Readerstate: Card Retained\n");
			break;
		default:
			printf("Readerstate: %d\n", readerState);
		}

		EFT_GetCardsRetained(h, &cardsRetained);
		{
			if (cardsRetained > 0)
			{
				printf ("*** %d Cards Retained ***", cardsRetained);
				/* reset flag */
				EFT_PutCardsRetained(h, 0);
			}
		}


		break;

	case 'd': /* display applications */
	
		r = EFT_GetApplicationsCount(h, &nCount);
		if (r != 0)
		{
			printf("applications not ipmlemented yet\n");
		}
		else
		{
			printf("number of applications: %d\n", nCount);
			for (int i = 0; i < nCount; i++)
			{
				EFT_GetApplicationsProperty(h, i, "application-key", msg, sizeof(msg));
				printf ("   %s", msg);
				EFT_GetApplicationsProperty(h, i, "application-name", msg, sizeof(msg));
				printf ("   %s", msg);
				EFT_GetApplicationsProperty(h, i, "currency", msg, sizeof(msg));
				printf ("   %s", msg);
				EFT_GetApplicationsProperty(h, i, "application-type", msg, sizeof(msg));
				printf ("   %s\n", msg);
			}
		}
		break;

	case '-': /* disconnect */
		printf("disconnecting EFT\n");
		r = EFT_Disconnect(h);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Disconnect returned error %d: %s\n", r, msg);
			break;
		}
		break;



	case '0': /* exit */
		goto by;
		break;

	default:
		goto retryread0;
	}
	goto retryread;


by:

	bCompleteThreadRunning = false;
	EFT_Disconnect(h);
	EFT_DestroySession(h);
	printf("*** BYE ***\n");

#ifdef WIN32
	if (hCompleteThread)
	  CloseHandle(hCompleteThread);
#endif

	return 0;
}

