/** 
 * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
 * 
 */

package hwb1;

import javacard.framework.*;

/**
 * Applet class
 * 
 * @author <user>
 */
public class MyApplet extends Applet {
    final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
    final static short SW_VERIFICATION_FAILED = 0x6300;
    final static byte PIN_TRY_LIMIT =(byte)0x03;
    final static byte MAX_PIN_SIZE =(byte)0x08;
    private byte[] authorName = new byte[] { 'M', 'a', 'n', 'u', 'e', 'l' };
    private short storageLength;
    private byte[] storage = new byte[] {};
    private OwnerPIN pin;

    /**
     * Installs this applet.
     * 
     * @param buffer  the array containing installation parameters
     * @param bOffset the starting offset in bArray
     * @param bLength the length in bytes of the parameter data in bArray
     */
    public static void install(byte[] buffer, short offset, byte length) {
        byte aidLength = buffer[offset];

        short controlInfoOffset = (short)(offset + 1 + aidLength);
        byte controlInfoLength = buffer[controlInfoOffset];
        
        short dataOffset = (short)(controlInfoOffset + 1 + controlInfoLength);
        byte dataLength = buffer[dataOffset];

        new MyApplet(buffer, (short)(dataOffset + 1), dataLength);
    }

    /**
     * Only this class's install method should create the applet object.
     *
     * @param bArray  the array containing installation parameters
     * @param bOffset the starting offset in bArray
     * @param bLength the length in bytes of the parameter data in bArray
     */
    protected MyApplet(byte[] appletData, short dataOffset, byte dataLength) {
        pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);
        pin.update(appletData, dataOffset, dataLength);
        register();
    }

    public void deselect() {
        super.deselect();
        pin.reset();
    }

    /**
     * Processes an incoming APDU.
     * 
     * @see APDU
     * @param apdu the incoming APDU
     */
    public void process(APDU apdu) {
        byte[] buffer = apdu.getBuffer();
        
        if (selectingApplet()) {
            if (pin.getTriesRemaining() == 0) {
                ISOException.throwIt(SW_VERIFICATION_FAILED);
            } else {
                ISOException.throwIt(ISO7816.SW_NO_ERROR);
            }
        } else {
            if (buffer[ISO7816.OFFSET_CLA] != (byte)0x80) {
                ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
            } else {
                boolean authenticationRequired = false;
                byte instruction = buffer[ISO7816.OFFSET_INS];

                switch (instruction) {
                    case 0x02:
                    case 0x04:
                        authenticationRequired = !pin.isValidated();
                        break;
                }

                if (authenticationRequired) {
                    ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
                }
                else {
                    short length;

                    switch (instruction) {
                        case 0x00:
                        case 0x04:
                            byte[] response;
                            length = apdu.setOutgoing();

                            if (instruction == 0x00) {
                                response = authorName;
                                length = (short)response.length;
                            } else {
                                response = storage;

                                if (length > storageLength) {
                                    ISOException.throwIt((short)(ISO7816.SW_CORRECT_LENGTH_00 | storageLength));
                                }
                            }

                            apdu.setOutgoingLength(length);
                            apdu.sendBytesLong(response, (short) 0, length);
                            break;
                        case 0x02:
                            storageLength = apdu.setIncomingAndReceive();

                            if (storageLength > 20) {
                                ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
                            } else {
                                Util.arrayCopy(buffer, ISO7816.OFFSET_CDATA, storage, (short) 0, storageLength);
                            }
                            break;
                        case 0x20:
                            length = apdu.setIncomingAndReceive();

                            if (!pin.check(buffer, (short) ISO7816.OFFSET_CDATA, (byte)length)) {
                                ISOException.throwIt(SW_VERIFICATION_FAILED);
                            }
                            break;
                        default:
                            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
                            break;
                    }	
                }
                
                ISOException.throwIt(ISO7816.SW_NO_ERROR);
            }
        }
    }
}
