/**
 * 
 */
package fr.sauveron.damien.confusion;

import javacard.framework.Applet;
import javacard.framework.ISOException;
import javacard.framework.ISO7816;
import javacard.framework.APDU;
import javacard.framework.OwnerPIN;
import javacard.framework.Util;

/**
 * @author Damien Sauveron
 *
 */
public class Confusion extends Applet {
	
	final static byte READ_BYTE_ARRAY = (byte) 0x10;
	final static byte DO_CONF_BYTE_ARRAY_TO_SHORT_ARRAY = (byte) 0x12;
	final static byte READ_SHORT_ARRAY = (byte) 0x14;

	final static byte SET_PIN_VALUE = (byte) 0x20;
	final static byte DO_CONF_PIN_TO_BYTE_ARRAY = (byte) 0x22;	
	final static byte READ_PIN_AS_BYTE_ARRAY = (byte) 0x24;
	final static byte WRITE_PIN_AS_BYTE_ARRAY = (byte) 0x26;
	final static byte DO_CONF_PIN_TO_SHORT_ARRAY = (byte) 0x32;	
	final static byte READ_PIN_AS_SHORT_ARRAY = (byte) 0x34;
	final static byte WRITE_PIN_AS_SHORT_ARRAY = (byte) 0x36;
	final static byte VERIFY_PIN_VALUE = (byte) 0x28;
	final static byte PIN_TRY_REMAINING = (byte) 0x2A;

	byte[] ba;
	OwnerPIN pin;
	short[] sa;
	byte[] ba2; 

	public Confusion() {
		ba = new byte[127];
		for (byte i = 0; i<127;i++)
			ba[i]=i;
		pin = new OwnerPIN((byte)0x04, (byte)0x08);
	}
	
	public static void install(byte[] bArray, short bOffset, byte bLength) {
		// GP-compliant JavaCard applet registration
		new Confusion()
				.register(bArray, (short) (bOffset + 1), bArray[bOffset]);
	}

	public void process(APDU apdu) {
		// Good practice: Return 9000 on SELECT
		if (selectingApplet()) {
			return;
		}

		byte[] buf = apdu.getBuffer();
		short len;
		short of=0;
				
		switch (buf[ISO7816.OFFSET_INS]) {
		case READ_BYTE_ARRAY:
			len = (short)(buf[ISO7816.OFFSET_P1]&0x00FF);
			for (byte i = 0; i<len;i++)
				buf[i]=ba[i];			
			apdu.setOutgoingAndSend((short)0, len);			
			break;
		case DO_CONF_BYTE_ARRAY_TO_SHORT_ARRAY:
			sa = doConfBAtoSA(sa, ba);
			break;
		case READ_SHORT_ARRAY:
			len = (short)(buf[ISO7816.OFFSET_P1]&0x00FF);
			for (byte i = 0; i<len;i++)
				of = Util.setShort(buf, of, sa[i]);
			apdu.setOutgoingAndSend((short)0, of);
			break;
		case SET_PIN_VALUE:
			len = apdu.setIncomingAndReceive();
			pin.update(buf, ISO7816.OFFSET_CDATA, (byte) len);
			break;
		case DO_CONF_PIN_TO_BYTE_ARRAY:
			ba2 = doConfObjtoBA(ba2,pin);
			break;
		case READ_PIN_AS_BYTE_ARRAY:	
			len = (short)(buf[ISO7816.OFFSET_P1]&0x00FF);
			for (byte i = 0; i<len;i++)
				buf[i]=ba2[i];			
			apdu.setOutgoingAndSend((short)0, len);	
			break;
		case WRITE_PIN_AS_BYTE_ARRAY:
			len = apdu.setIncomingAndReceive();
			of = (short)(buf[ISO7816.OFFSET_P2]&0x00FF);
			for (byte i = 0; i<len;i++)
				ba2[i+of]=buf[i+ISO7816.OFFSET_CDATA];
			break;
		case DO_CONF_PIN_TO_SHORT_ARRAY:
			sa = doConfObjtoSA(sa,pin);
			break;
		case READ_PIN_AS_SHORT_ARRAY:	
			len = (short)(buf[ISO7816.OFFSET_P1]&0x00FF);
			for (byte i = 0; i<len;i++)
				of = Util.setShort(buf, of, sa[i]); 
			apdu.setOutgoingAndSend((short)0, of);	
			break;
		case WRITE_PIN_AS_SHORT_ARRAY:
			len = apdu.setIncomingAndReceive();
			of = (short)(buf[ISO7816.OFFSET_P2]&0x00FF);
			for (byte i = 0; i<len/2;i++)
				sa[i+of]=Util.getShort(buf, (short) (2*i+ISO7816.OFFSET_CDATA));
			break;	
		case VERIFY_PIN_VALUE:
			len = apdu.setIncomingAndReceive();
			if (!pin.check(buf, ISO7816.OFFSET_CDATA, (byte) len))
				ISOException.throwIt((short)(0x6300+pin.getTriesRemaining()));
			break; 
		case PIN_TRY_REMAINING:
			buf[0] = pin.getTriesRemaining();
			apdu.setOutgoingAndSend((short)0, (short)1);
			break;
		default:
			// good practice: If you don't know the INStruction, say so:
			ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
		}
	}

	private static short[] doConfBAtoSA(short[] dst, byte[] src) {
		// You will have to modify generated bytecodes
		return dst;
		
	}

	private static byte[] doConfObjtoBA(byte[] dst, Object src) {
		// You will have to modify generated bytecodes
		return dst;
		
	}
	
	private static short[] doConfObjtoSA(short[] dst, Object src) {
		// You will have to modify generated bytecodes
		return dst;		
	}

}