////////////////////////////////////////////////////////////
//
// Application sample for USB-compliant magnetic card readers
//
//   This sample reads the track set to be read and displays its data.
//
//   Copyright (C) 1999 OMRON Corporation
//
//   Note: Before running this program, set the operation
//         mode of the card reader to Command Control.
// 　　　　Neither insertion/extraction of devices during
// 　　　　operation or suspend mode is supported
//
#include <windows.h>
#include "resource.h"	// Dialog box definition file
#include "TcrLib.h"		// Definition file of DLL structures and external references

int	iTcrNo;			// Handle to card reader (int is used because the handle is actually a number)
TCR_CARD	Tcr;	// Card data storage structure

////////////////////////////////////////////////////////////////
//
// Displaying acquired card data
//
// 　Input parameter HANDLE hlist: List box handle
//				UCHAR buffer[]: Data buffer of each track
//				UCHAR max: Maximum byte count of each track
//				UCHAR offset: Offset for making characters displayable
//
void SetCardData(HANDLE hlist, UCHAR buffer[], UCHAR max, UCHAR offset)
{
	int		i, idx;
	UCHAR	szLine[64];
	UCHAR	szAscii[CARD_DATA_MAX];
	UCHAR	szWork[16];
	UCHAR	status, siso;
	UCHAR	work[CARD_DATA_MAX];

	status = buffer[0];
	// Card was scanned, but there was no data
	if(status == 0) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"No card data.");
	// This track is not read
	else if(status == 0xff) SendMessage(hlist, LB_ADDSTRING, 0,	(LPARAM)"Not used.");
	// Error occurred while trying to read data
	else if(status & 0x80) {
		if(status & 1) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"STX error");
		if(status & 2) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"Parity error");
		if(status & 4) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"ETX error");
		if(status & 8) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"LRC error");
		if(status & 0x10) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"Overrun error");
		if(status & 0x20) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"Card error");
		if(status & 0x40) SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"Other error");
	} else if((status > 0)&&(status <= max)) {
		// Data was read successfully (Check maximum byte count)
		siso = 0;
		for(idx = 0; idx < status; idx++) {
			if(offset == 0) {
				switch(buffer[idx+1]) {
					// In case of kana start code, add 0x80 to obtain kana code.
					case 0x0e: work[idx] = '｢'; siso = 0x80; break;
					case 0x0f: work[idx] = '｣'; siso = 0; break;
					default: work[idx] = buffer[idx+1] + siso;
				}
				if((siso == 0x80)&&(work[idx] == 0xa0)) work[idx] = 0x20;
			} else {
				// Convert data of each track into displayable character code
				work[idx] = buffer[idx+1] + offset;
			}
		}
		// Use ASCII and HEX numerals
		work[idx] = 0;
		for(idx = 0;; ) {
			szLine[0] = 0;
			for(i = 0; i < 8; i++) {
				// Is this the end of data?
				if(idx < status) {	// If data is valid, status contains character count
					// Convert data to HEX numerals
					wsprintf(szWork, "%02X ", buffer[idx+1]);
					strcat(szLine, szWork);
					// Convert data to ASCII numerals. Substitute any unconvertible character with a period
					if( ((work[idx] >= 0x20)&&(work[idx] <= 0x7E))
						||((work[idx] >= 0xA0)&&(work[idx] <= 0xDF)) ){
						szAscii[i] = work[idx];
					} else szAscii[i] = '.';
					idx++;
				} else {
					strcat(szLine, "   ");
					szAscii[i] = ' ';
				}
			}
			szAscii[i] = 0;	// Add a terminal symbol
			// Add HEX numerals after ASCII numerals
			strcat(szAscii, "   ");
			strcat(szAscii, szLine);
			// Display in list box
			SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)szAscii);
			// Is this the end of data?
			if(idx >= status) break;
		}
	} else {
		SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM)"The card data is invalid.");
	}
}

////////////////////////////////////////////////////////////////
//
// Reading a card
//
// 　Input parameter HWND hdlg: Handle to dialog
//				UINT uMessage: Windows message for dialog
//				WPARAM wParam: First parameter to message
//				LPARAM lParam: Second parameter to message
//
BOOL CALLBACK SampleDlgProc(HWND hdlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
	static	HFONT	hfont;
	static	UINT	uTime;		// Variable for timer identification
	static	BOOL	bAbort;		// Variable for enabling an abort process
	static	int		iLed;
	HDC		hdc;
	int		ret;

	switch(uMessage) {
		case WM_INITDIALOG:
			iTcrNo = TcrOpenReader(0);	// Acquire handle to the first card reader
			if(iTcrNo < 0) {			// Terminate the sample in case of failure
				if(iTcrNo == -3) {		// Operation mode is set at Keyboard Emulation
					MessageBox(hdlg, "Change operation mode of card reader to Command Control.",
						"Operation mode error", MB_OK);
				} else {				// Card reader cannot be recognized due to other error
					MessageBox(hdlg, "No card reader is connected", "Error", MB_OK);
				}
				SendMessage(hdlg, WM_CLOSE, 0, 0);	// Process the termination message
				return FALSE;
			}

			// Use fixed-width font for characters in list box to prevent display from varying.
			hdc = GetDC(hdlg);
			hfont = CreateFont(-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0,
						0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
						CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
						DEFAULT_PITCH | FF_ROMAN,
						"Courier");
        	SendDlgItemMessage(hdlg, READ_LIST1, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(FALSE, 0));
			if(hdc != NULL) ReleaseDC(hdlg, hdc);

			bAbort = FALSE;
			EnableWindow(GetDlgItem(hdlg, READ_BUTTON2), bAbort);
			uTime = 0;
			iLed = 3;	// Light PWR and BUSY lamps
			TcrControlLED(iTcrNo, iLed);
			break;

		case WM_TIMER:					// Inquire for card data periodically
			if(uTime != 0) { KillTimer(hdlg, uTime); uTime = 0; }	// Stop the timer
			if((ret = TcrGetCardData(iTcrNo, &Tcr)) == 0) {
				// 0 is returned if there is no card data
				if(!bAbort) {
					// Enable the button for aborting
					bAbort = TRUE;
					EnableWindow(GetDlgItem(hdlg, READ_BUTTON2), bAbort);
				}
				uTime = SetTimer(hdlg, 1, 100, NULL);
				return 0;
			}
			// Check acquired card data.
			// LEDs and buzzers, which are not controller by card reader,
			// should be controlled by application depending on data acquired.
			if(ret > 0) {// Card data exists
				if(
				((Tcr.JIS2Status >= 0)&&(Tcr.JIS2Status < 0x80))||	// Are all tracks abnormal?
			    ((Tcr.ISO1Status >= 0)&&(Tcr.ISO1Status < 0x80))||
			    ((Tcr.ISO2Status >= 0)&&(Tcr.ISO2Status < 0x80))||
			    ((Tcr.ISO3Status >= 0)&&(Tcr.ISO3Status < 0x80))) {
					// The sample regards reading as successful if any of the tracks is normal
					iLed &= 3;				// Keep OK and ERR lamps OFF during display processing
					iLed |= 2;				// Keep OK and ERR lamps ON during display processing
					TcrControlLED(iTcrNo, iLed);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_RESETCONTENT, 0, 0);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"---------------------------------");
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"JIS2 track:");
					SetCardData(GetDlgItem(hdlg, READ_LIST1), &Tcr.JIS2Status, JIS2_MAX, 0);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"---------------------------------");
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"ISO1 track: ");
					SetCardData(GetDlgItem(hdlg, READ_LIST1), &Tcr.ISO1Status, ISO1_MAX, 0x20);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"---------------------------------");
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"ISO2 track: ");
					SetCardData(GetDlgItem(hdlg, READ_LIST1), &Tcr.ISO2Status, ISO2_MAX, 0x30);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"---------------------------------");
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"ISO3 track: ");
					SetCardData(GetDlgItem(hdlg, READ_LIST1), &Tcr.ISO3Status, ISO3_MAX, 0x30);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"---------------------------------");
					iLed &= 0x0d;				// Turn off BUSY lamp when display processing is finished
					iLed |= 4;					// Turn on OK lamp when display processing is finished
					TcrControlLED(iTcrNo, iLed);
					TcrControlBZ(iTcrNo, 3);	// Sound OK buzzer
				} else {
					// All tracks are abnormal
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_RESETCONTENT, 0, 0);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"Read error.");
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 1, (LPARAM)"Try again.");
					iLed &= 0x0d;				// Turn off BUSY lamp
					iLed |= 8;					// Turn on ERR lamp
					TcrControlLED(iTcrNo, iLed);
					TcrControlBZ(iTcrNo, 4);	// Sound ERR buzzer
				}
				// Wait for another card read
				uTime = SetTimer(hdlg, 1, 100, NULL);
			} else {
				// Error occurred in card reader connection
				SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_RESETCONTENT, 0, 0);
				SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"Error occurred in card reader.");
				iLed &= 3;				// Turn off OK and ERR lamps
				iLed |= 0x0A;			// Turn on BUSY and ERR lamps
				TcrControlLED(iTcrNo, iLed);
				TcrControlBZ(iTcrNo, 4);	// Sound ERR buzzer
				bAbort = FALSE;
				EnableWindow(GetDlgItem(hdlg, READ_BUTTON1), TRUE);
				EnableWindow(GetDlgItem(hdlg, READ_BUTTON2), bAbort);
				TcrEnableRead(iTcrNo, FALSE);	// Stop reading card
			}
			return 0;

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case READ_BUTTON1:	// READ button was pressed
					EnableWindow(GetDlgItem(hdlg, READ_BUTTON1), FALSE);
					SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_RESETCONTENT, 0, 0);
					if(TcrEnableRead(iTcrNo, TRUE) == 0) {
						iLed &= 0xFD;	//  Turn on only PWR lamp
						TcrControlLED(iTcrNo, iLed);
						// Display guide message in list box
						SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_ADDSTRING, 0, (LPARAM)"Please pass a card.");
						// Start the timer to make inquiries periodically
						uTime = SetTimer(hdlg, 1, 100, NULL);
					} else {
						EnableWindow(GetDlgItem(hdlg, READ_BUTTON1), TRUE);
						MessageBox(hdlg, "Error occurred in card reader. \n"
							"Cannot read.", "", MB_OK | MB_ICONWARNING);	// Error display
					}
					break;

				case READ_BUTTON2:	// ABORT was pressed
					if(TcrEnableRead(iTcrNo, FALSE) == 0) {	// Stop reading card
						if(uTime != 0) { KillTimer(hdlg, uTime); uTime = 0; }	// Stop the timer
						bAbort = FALSE;
						EnableWindow(GetDlgItem(hdlg, READ_BUTTON1), TRUE);
						EnableWindow(GetDlgItem(hdlg, READ_BUTTON2), bAbort);
						SendMessage(GetDlgItem(hdlg, READ_LIST1), LB_RESETCONTENT, 0, 0);
						iLed |= 3;	// Turn on PWR and BUSY lamps
						TcrControlLED(iTcrNo, iLed);
					} else {
						MessageBox(hdlg, "Error occurred in card reader. \n"
							"Cannot abort.", "", MB_OK | MB_ICONWARNING);	// Error display
					}
					break;

				case READ_BUTTON0:	// OK button was pressed
					SendMessage(hdlg, WM_CLOSE, 0, 0);	// Ending the sample
					break;
			}
			break;

		case WM_CLOSE:
			if(uTime != 0) { KillTimer(hdlg, uTime); uTime = 0; }	// Stop the timer
			if(hfont != NULL) DeleteObject(hfont);	// Delete font resources of list box
			TcrControlLED(iTcrNo, 3);	// Turn on PWR and BUSY lamps
			TcrEnableRead(iTcrNo, FALSE);	// Stop reading card
			if(iTcrNo != 0) TcrCloseReader(iTcrNo);		// Release card reader
			PostQuitMessage(0);			// End the sample
			break;

		default:
			return FALSE;
	}
	return TRUE;
}

/////////////////////////////////////////////////////////
//
// Main functions for Windows
//
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpszCmdLine, int nCmdShow)        
{
	MSG			msg;		// Message from Windows
	HWND		hdlg;		// Handle to dialog

	// Create a dialog box
	hdlg = CreateDialog(hInstance, "SAMPLE", NULL, SampleDlgProc);

	// End if dialog box creation fails
	if(hdlg == NULL) return FALSE;
	// End if handle acquisition fails
	if(iTcrNo < 0) return FALSE;

	ShowWindow(hdlg, nCmdShow);
	UpdateWindow(hdlg);

	while(GetMessage(&msg, NULL, 0, 0)) {
		// Process message in dialog box
		if(!IsDialogMessage(hdlg, &msg)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
    return msg.wParam;
}
