/****************************************************************************************
 *
 *	TEST_EFTAPI.CPP
 *
 *	demonstrate the programming with EFTAPI interface of MPD
 *  sync and async modes, polling
 *
 *	Windows and Linux
 *  
 ****************************************************************************************/

#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

void StartTransactionThread(void);


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


EFT_HANDLE h;


/****************************************************************************************
 *
 *	main
 *  
 *	loops for input
 *	 
 ****************************************************************************************/



int main()
{
	int ch;
	int r;
	long nCount;
	long readerState = -1, newReaderState;
	long deviceState = -1, newDeviceState;
	long newActivationState;
	long cardsRetained;
	long ecrSequence;
	char msg[1000];
	char receipt[4000];

	r = EFT_CreateSession(&h);
	if (r != 0)
	{
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("EFT_CreateSession returned error %d: %s\n", r, msg);
		goto by;
	}

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

	/* set driver adress */
	char adr[32];
	printf("DriverAddress (0 = default = local) ? ");
	r = scanf("%s", &adr);

	if (strlen(adr) > 5)
	{
		r = EFT_PutDriverAddress(h, adr);
	}

	/* Connect to be shure, the driver is runnung */
	r = EFT_Connect(h);
	if (r != 0)
	{
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("EFT_Connect returned error %d: %s\n", r, msg);
		goto by;
	}
	printf("EFT_Connected\n\n");
	

retryread0:
	printf(
	"MPD EFTAPI Test Programm\n"
	"o) Open\n"
	"t) Transaction sync\n"
	"z) Transaction async\n"
	"i) Transaction in a thread\n"
	"c) Close\n"
	"b) Balance\n"
	"p) Print Receipt\n"
	"s) Display Device-Status\n"
	"d) Display Applications\n"
	"2) Initialization\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)
		{
			/* in the language, set in the mpd */
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Open returned error %d: %s\n", r, msg);

			/* in the language, set here */
			const char * t = 0;
			EFT_GetErrorText(h, "de", r, &t);
			printf("EFT_Open gibt Fehler %d: %s\n", r, t);

			break;
		}
		EFT_GetECRSequence(h, &ecrSequence);
		printf("EFT is open, ecr-sequence: %d\n", ecrSequence);
		break;

	case 't':/* transaction sync */
		printf("start transaction sync\n");
		r = EFT_Transaction(h, "debit", 200, 0);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Transaction returned error %d: %s\n", r, msg);
			break;
		}
		printf("Trx is ok -> Commit\n");
		r = EFT_Commit(h, true);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_Commit returned error %d: %s\n", r, msg);
			break;
		}
		break;

	case 'z': /* transaction async */
		printf("start transaction async\n");
		printf("to abort: press 'a'\n");
		EFT_PutAsync(h, 1);
		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);
			EFT_PutAsync(h, 0);
			break;
		}

		r = EFT_E_TIMEOUT;
		readerState = -1;
		while (r == EFT_E_TIMEOUT)
		{
			/* check if abort is pressed */
			if (_kbhit())
			{
				ch = _getch();
				if (ch == 'a')
				{
					/* abort */
					printf("abort transaction\n");
					r = EFT_Abort(h);
				}
			}
			
			/* wait for events from driver for 500ms */
			r = EFT_Complete(h, 500);

            /* display changed ReaderStatus */
			EFT_GetDeviceEventCode(h, &newReaderState);
			if (newReaderState != readerState)
			{
				readerState = newReaderState;
				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("Readerstate: Card Retained\n");
					break;
				default:
					printf("Readerstate: %d\n", readerState);
				}
			}
		}
		EFT_PutAsync(h, 0);
		
		if (r == 0)
		{
			/* Trx is ok -> Commit */
			printf("Trx is ok -> Commit\n");
			r = EFT_Commit(h, true);
			if (r == 0)
			{
				int outc;
				r = EFT_GetOutcome(h, &outc);

				printf("Commit done: Outcome = %d\n", outc);
			}
			else
				printf("Commit returned error %d\n", r);
		}
		else
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("Trx returned error %d: %s\n", r, msg);
		}
		break;

	case 'i': /* transaction in a thread  */
        printf("start transaction in a thread\n");
        printf("this transaction can be aborted with command 'a'\n");
        StartTransactionThread();
        break;

	case 'a': /* abort the transaction in a thread  */
		r = EFT_Abort(h);
        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;
		}
		printf("EFT is closed\n");

		break;

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

	case '2': /* initialization */
		printf("initializing EFT\n");
		EFT_PutAsync(h, 0);
		r = EFT_DeviceControl(h, 2, 2);
		if (r != 0)
		{
			EFT_GetExceptionMessage(h, msg, sizeof(msg));
			printf("EFT_DeviceControl returned error %d: %s\n", r, msg);
			break;
		}
		printf("initializing EFT finished\n");

		break;

	case 'p': /* rpint receipt */
		EFT_GetReceiptCopyCount(h, &nCount);
		if (nCount > 0)
		{
			r = EFT_GetReceiptText(h, receipt, sizeof(receipt));
			if (r != 0)
			{
				/* probably bufferoverflow */
				const char * errorText;
				EFT_GetErrorText(h, "en", r, &errorText);
				printf("EFT_GetReceiptText returned error %d: %s\n", r, errorText);
				break;
			}
			/* print out receipt ....*/
			printf (receipt);
			printf("\n");
		}
		else
		{
			printf("ReceiptCopyCount = 0\n");
		}
		break;

	case 's': /* display device status */
		r = EFT_Complete(h, 1); /* must be done to read messages from driver */

		EFT_GetActivationState(h, &newActivationState);
		switch (newActivationState)
		{
		case shiftClosed:
			printf("Shift state: closed\n");
			break;
		case shiftOpen:
			printf("Shift state: open\n");
			break;
		}

		EFT_GetDeviceStatusCode(h, &newDeviceState);
		switch (newDeviceState)
		{
		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, &newReaderState);
		switch (newReaderState)
		{
		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("Readerstate: Card Retained\n");
			break;
		default:
			printf("Readerstate: %d\n", newReaderState);
		}
		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\n", msg);
			}
		}
		break;



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

	default:
		goto retryread0;
	}
	goto retryread;


by:
	r = EFT_DestroySession(h);
	printf("*** Session Destroyed ***\n\npress any key to exit\n");
by2:	
	ch = _getch();
	if (ch == 10) goto by2;

	return 0;
}


#ifdef WIN32
DWORD WINAPI TransactionThread(void *pCall)
#else
void * TransactionThread(void *pCall)
#endif
{
    int r;
    long readerState = 0;
    long newReaderState;
	long complCommand;
	char msg[1000];
	printf("...TransactionThread: start transaction async...\n");
	EFT_PutAsync(h, 1);
	r = EFT_Transaction(h, "debit", 250, 0);
	if (r != 0)
	{
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("...TransactionThread: EFT_Transaction returned error %d: %s\n", r, msg);
		EFT_PutAsync(h, 0);
		return 0;
	}

    do{
        r = EFT_Complete(h, -1);

        /* display changed ReaderStatus */
		EFT_GetDeviceEventCode(h, &newReaderState);
		if (newReaderState != readerState)
		{
			readerState = newReaderState;
			switch (readerState)
			{
			case readerEmpty:
				printf("...TransactionThread: Readerstate: Empty\n");
				break;
			case readerLoaded:
				printf("...TransactionThread: Readerstate: Card Loaded\n");
				break;
			case readerEjected:
				printf("...TransactionThread: Readerstate: Card Ejected\n");
				break;
			case readerCardRetension:
				printf("...TransactionThread: Readerstate: Card Retained\n");
				break;
			default:
				printf("...TransactionThread: Readerstate: %d\n", readerState);
			}
		}
		EFT_GetCompletedCommand(h, &complCommand);

    }while (complCommand != ccTransaction);
        


	EFT_PutAsync(h, 0);
	if (r == 0)
	{
		/* Trx is ok -> Commit */
		printf("...TransactionThread: Trx is ok -> Commit\n");
		r = EFT_Commit(h, true);
		if (r == 0)
			printf("...TransactionThread: Commit done\n");
		else
			printf("...TransactionThread: Commit returned error %d\n", r);
		printf("...TransactionThread: finished ok\n");
	}
	else
	{
		EFT_GetExceptionMessage(h, msg, sizeof(msg));
		printf("...TransactionThread: Trx returned error %d: %s\n", r, msg);
	}

	return 0;
}

#ifdef WIN32
void StartTransactionThread(void)
{
	DWORD dwTreadId;
		
	CreateThread( NULL, 5000, TransactionThread,
						0,
						0,
						&dwTreadId);
	
}
#else
void StartTransactionThread(void)
{
	pthread_t dwTreadId;
		
	int r = pthread_create( &dwTreadId, NULL,TransactionThread, 0);
	
}
#endif

