package controllers;


import java.io.IOException;

import android.os.SystemClock;

import exceptions.TransactionAbortedException;
import exceptions.TransactionCanceledException;
import exceptions.TransactionException;
import com.datecs.pinpad.Pinpad;
import com.datecs.pinpad.Pinpad.MagstripeListener;
import com.datecs.pinpad.Pinpad.SmartCardListener;
import com.datecs.pinpad.PinpadException;
import com.datecs.pinpad.Pinpad.PINEntryCompleteListener;

import utils.StringUtils;



/**
 * A class that implements helper methods that implements various Pinpad functionality.
 */
public class PinpadHelper {
	// Show initial application screen.
	public static final void initScreen(Pinpad pinpad) throws IOException, PinpadException {
	    pinpad.uiInitScreen();
	    pinpad.uiKeyboardControl(false);
	    
	    if (pinpad.getDisplayHeight() > 32) {
	        pinpad.uiOpenTextWindow(0, 0, 16, 4, Pinpad.FONT_8X16, Pinpad.CP_LATIN1);
	    } else {
	        // Small screen
	        pinpad.uiOpenTextWindow(0, 0, 16, 2, Pinpad.FONT_8X16, Pinpad.CP_LATIN1);
	    }
	}
		
	// Clear screen. 
	public static final void clearScreen(Pinpad pinpad) throws PinpadException, IOException {
	    pinpad.sysStatusLine(false);
        pinpad.uiStopAnimation(-1);
        pinpad.uiFillScreen(Pinpad.COLOR_WHITE);
	}
	
	/** Play sound that imitate 'SUCCESS'. */  
	public static void beepSuccess(Pinpad pinpad) throws PinpadException, IOException {	    
	    pinpad.sysBeep(5300, 500, 50);
	    SystemClock.sleep(500);
	}
	
	/** Play sound that imitate 'FAILURE'. */
	public static void beepFailure(Pinpad pinpad) throws PinpadException, IOException {
	    pinpad.sysBeep(2000, 100, 100);
	    SystemClock.sleep(20);
	    pinpad.sysBeep(2000, 100, 100);
	    SystemClock.sleep(20);
        pinpad.sysBeep(2000, 100, 100);
        SystemClock.sleep(20);
        pinpad.sysBeep(2000, 100, 100);
        SystemClock.sleep(20);
        pinpad.sysBeep(2000, 100, 100);
        SystemClock.sleep(20);  
    }
	
	/** Play sound that imitate 'ATTENTION'. */
    public static void beepAttention(Pinpad pinpad) throws PinpadException, IOException {
        pinpad.sysBeep(2000, 250, 50);
        SystemClock.sleep(250);
    }
    
	/** Shows that transaction is aborted. */
	public static final void showAborted(Pinpad pinpad) throws PinpadException, IOException {
	    clearScreen(pinpad);	    
		pinpad.uiDrawString("\u0001  TRANSACTION\n    ABORTED");
		beepFailure(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
	
	/** Shows that transaction is canceled. */
	public static final void showCanceled(Pinpad pinpad) throws PinpadException, IOException {
	    clearScreen(pinpad);    
		pinpad.uiDrawString("\u0001  TRANSACTION\n   CANCELLED");
		beepFailure(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
    
	/** Shows that transaction complete successful. */
	public static final void showSuccessful(Pinpad pinpad) throws PinpadException, IOException  {
	    clearScreen(pinpad);    	
		pinpad.uiDrawString("\u0001  TRANSACTION\n   SUCCESSFUL");
		beepSuccess(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
	
	/** Shows that transaction complete successful. */
	public static final void showMessage(Pinpad pinpad, String message) throws PinpadException, IOException  {
	    clearScreen(pinpad);    	
		pinpad.uiDrawString("\u0001  "+message);
		beepSuccess(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
	
	/** Shows that transaction is denied. */
	public static final void showDenied(Pinpad pinpad) throws PinpadException, IOException  {
	    clearScreen(pinpad);
		pinpad.uiDrawString("\u0001  TRANSACTION\n    DENIED");
		beepFailure(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
	
	/** Shows that PIN is valid. */
	public static final void validPIN(Pinpad pinpad) throws PinpadException, IOException {
	     
		pinpad.uiDrawString("\u0001  VALID PIN");
		SystemClock.sleep(1000);
		showBusy(pinpad);
		
    }
	
	/** Shows that PIN is invalid. */
	public static final void invalidPIN(Pinpad pinpad) throws PinpadException, IOException {
	    clearScreen(pinpad);    
		pinpad.uiDrawString("\u0001   INVALID PIN");
		beepFailure(pinpad);
		SystemClock.sleep(1000);
		initScreen(pinpad);
    }
    
	public static final void invalidPINRetry(Pinpad pinpad, String amount) throws PinpadException, IOException, TransactionCanceledException, TransactionAbortedException {
	    
		
	/*	do{
			clearScreen(pinpad);
			pinpad.uiDrawString("\u0001  INVALID PIN");
			beepFailure(pinpad);
			SystemClock.sleep(1000);
			enterPin(pinpad, amount);
			showBusy(pinpad);
			pinpad.emvVerifyPinOffline();
			int emvStatus = pinpad.emvGetLastStatus();
			if (emvStatus == EMVStatusCodes.EMV_SUCCESS) {
				authResult = Pinpad.EMV_AUTH_RESULT_SUCCESS;
				validPIN(pinpad);
				break;
			} else {
				authResult = Pinpad.EMV_AUTH_RESULT_FAILURE;
			}
			i++;
		}while(i < 3);*/
		
	   int authResult = -1;
	   int i=0;
	   for (i =0; i<2; i++) {
	    	clearScreen(pinpad);
	    	pinpad.uiDrawString("\u0001  INVALID PIN");
	    	
	    	beepFailure(pinpad);
	    	SystemClock.sleep(1000);
	    	enterPin(pinpad, amount);
	    	showBusy(pinpad);
//	    	pinpad.emvVerifyPinOffline();
//			int emvStatus = pinpad.emvGetLastStatus();
//			if (emvStatus == EMVStatusCodes.EMV_SUCCESS) {
//				authResult = Pinpad.EMV_AUTH_RESULT_SUCCESS;
//				validPIN(pinpad);
//				break;
//			} else {
//				authResult = Pinpad.EMV_AUTH_RESULT_FAILURE;
//				pinpad.uiDrawString("\u0001  INVALID PIN");
//				initScreen(pinpad);
//			}
	    }
	   
	   /*if (i == 0 && authResult == Pinpad.EMV_AUTH_RESULT_FAILURE){ //3 failed attempts
			pinpad.uiDrawString("\u0001  INVALID PIN");
			initScreen(pinpad);
		}*/
    }
    
	
	/** Shows that operation is in progress. */
	public static final void showBusy(Pinpad pinpad) throws PinpadException, IOException  {
	    clearScreen(pinpad);
    	pinpad.uiDrawString("\u0001   PLEASE WAIT");    	
    }
    
	/** Check if card is available. */
	public static final boolean isCardAvailable(Pinpad pinpad) throws PinpadException, IOException {
    	try {
			pinpad.scCheckCard(Pinpad.SC_SLOT_MAIN);			
		} catch (PinpadException e) {
			if (e.getErrorCode() == Pinpad.ERROR_NO_DATA) {
				return false;
			}
			throw e;
		}
    	
    	return true; 
    }
	
	/** Read key from Pinpad keyboard */
	public static final int readKey(Pinpad pinpad) throws PinpadException, IOException {
	    int key = 0;
	    
		pinpad.uiKeyboardControl(true);
		
		try {    		
    		key = pinpad.uiReadKey();    		
		} catch (PinpadException e) {
			if (e.getErrorCode() != Pinpad.ERROR_NO_DATA) {
			    throw e;
			}			
		}
				
		return key;
    }
	
	/** Enter PIN with the given amount value */
    public static final void enterPin(Pinpad pinpad, String amount) throws PinpadException,
            IOException, TransactionCanceledException, TransactionAbortedException {
	    final int[] result = new int[] { Pinpad.ERROR_NONE};
	    
	    pinpad.uiEnterPinAsync(0, 1, 20, '*', "AMT: N" + amount + "\nEnter PIN:", new PINEntryCompleteListener() {                     
            @Override
            public void onPINEntryComplete(int errorCode) {
                result[0] = errorCode;
                
                synchronized (result) {
                    result.notify();
                }
            }
        });      
	    
	    // Wait to complete PIN entry.
        synchronized (result) {
            try {                           
                result.wait();
            } catch (InterruptedException e) { }                            
        }
        
        if (result[0] != Pinpad.ERROR_NONE) {
            switch (result[0]) {
                case Pinpad.ERROR_CANCEL: throw new TransactionCanceledException("PIN canceled");
                case Pinpad.ERROR_TIMEOUT: throw new TransactionCanceledException("PIN timeout");
                default: throw new PinpadException(result[0]);
            }
        }
	}
	     
    /** Card not presents. */
   public static final int CARD_NOT_PRESENT = 0x00;    
    /** Magnetic card presents */
    public static final int CARD_MS = 0x01;
    /** Smart card presents */
    public static final int CARD_SC = 0x02;
    
    /** Wait to present card. Method returns the present card type. */
    public static final int presentCard(Pinpad pinpad) throws PinpadException, IOException, TransactionException {
        removeCard(pinpad);
        
        clearScreen(pinpad);
        pinpad.uiDrawString("\u0001 PLEASE INSERT\n      CARD");
        pinpad.scInit(Pinpad.SC_SLOT_MAIN, Pinpad.SC_VOLTAGE_5, Pinpad.SC_PROTOCOL_T0);
        pinpad.msStart();
        
        final int result[] = { CARD_NOT_PRESENT };   
        pinpad.setMagstripeListener(new MagstripeListener() {
            @Override
            public void onMagstripeRead() {
                System.out.println("Magstripe card is read");
                
                synchronized (result) {
                    result[0] += CARD_MS;
                    result.notify();
                }
            }
        });
        
        pinpad.setSmartCardListener(new SmartCardListener() {
            @Override
            public void onSmartCardRemoved() {
                System.out.println("Smart card is removed");
            }
            
            @Override
            public void onSmartCardInserted() {
                System.out.println("Smart card is inserted");
                
                synchronized (result) {
                    result[0] += CARD_SC;
                    result.notify();
                }
            }
        });
        
        do {
            int key = readKey(pinpad); 
            if (key == 'C') {
                throw new TransactionCanceledException("Transaction is canceled by user");
            }
            
            synchronized (result) {
                try {
                    result.wait(100);
                } catch (InterruptedException e) {                    
                }                
            }
            
            // On the combined reader (smart card and magnetic card at ones) the event for magnetic card comes before
            // smart card event. In that case we need to wait for additional 200ms for smart card event. 
            if ((result[0] & CARD_MS) != 0) {
                synchronized (result) {
                    try {
                        result.wait(200);
                    } catch (InterruptedException e) {                    
                    }                
                }
            }            
        } while (result[0] == CARD_NOT_PRESENT);                
        
        pinpad.setMagstripeListener(null);
        pinpad.setSmartCardListener(null);
        return result[0];        
    }
    
    /** Wait to remove card. */
	public static final void removeCard(Pinpad pinpad) throws PinpadException, IOException {
		pinpad.scInit(Pinpad.SC_SLOT_MAIN, Pinpad.SC_VOLTAGE_5, Pinpad.SC_PROTOCOL_T0);
		
		if (isCardAvailable(pinpad)) {
		    clearScreen(pinpad);
	        pinpad.uiDrawString("\u0001 PLEASE REMOVE\n      CARD");    
	    
	        do {
	            beepAttention(pinpad);
	        } while (isCardAvailable(pinpad));
		}	    
    }
	
	/** Show confirmation message. Method returns true on confirmed or false if canceled.*/
	public static final boolean confirm(Pinpad pinpad, String message) throws PinpadException, IOException {
		clearScreen(pinpad);
		pinpad.uiDrawString("\u0001" + message);
				
    	while (true) {
    		int key = readKey(pinpad);
    		
    		if (key == 'A') {
    			return true;
    		}
    		
    		if (key == 'C') {
    			return false;
    		}
    		
    		SystemClock.sleep(100);    			
    	}    	
	}
	
	/** Show items selection list. Method returns the selected item index. */
	public static final int select(Pinpad pinpad, String[] items) throws PinpadException, IOException, TransactionAbortedException {
		int lines = (pinpad.getDisplayHeight() > 32) ? 3 : 1;
		int start = 0;
		int index = 0;
		boolean redraw = true;
		
		pinpad.sysStatusLine(false);
    	pinpad.uiStopAnimation(-1);
    	
    	while (true) {
    		if (redraw) {
    			String s = new String("\u0001\u000B   SELECT APP   \u000C");    			
    			
    			for (int i = start; i < (start + lines) && i < items.length; i++) {
    				if (i == index) {
    					s += StringUtils.padRight(">" + items[i], 16, ' ').substring(0, 16); 
    				} else {
    					s += StringUtils.padRight(" " + items[i], 16, ' ').substring(0, 16);
    				}    				
    			}
    			pinpad.uiFillScreen(Pinpad.COLOR_WHITE);    
				pinpad.uiDrawString(s);
    			redraw = false;
    		}
    		
    		int key = readKey(pinpad);
    		
    		switch (key) {
    			case 'A': return index;
    			case 'C': throw new TransactionAbortedException("Selection is canceled by user");
    			case 'a': {
	    			if (index > 0) index--;
	    			if (start > index) start--;
	    			redraw = true;
	    			break;
	    		}
    			case 'b': {
    				if (index < (items.length - 1)) index++;
    				if ((index - start) >= lines) start++;    				
    				redraw = true;
    				break;
    			}
    			default: {
    				SystemClock.sleep(100);
    			}
    		}    		    		
    	}    			
	}	
	
}
