import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import java.awt.Font;
import javax.swing.JComboBox;
import javax.swing.JButton;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;

import Acos.Acos3;
import AcsCommon.*;
import Pcsc.*;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.JRadioButton;
import javax.swing.border.EtchedBorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;

import javax.swing.JTextField;
import javax.swing.JCheckBox;
import javax.smartcardio.CardException;
import javax.swing.ButtonGroup;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;
import java.awt.event.ActionEvent;

public class ACOS3Account extends JFrame implements ReaderEvent.TransmitApduHandler {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textFieldDebit;
	private JTextField textFieldCredit;
	private JTextField textFieldCertify;
	private JTextField textFieldRevoke;
	private JTextField textFieldAccount;
	private final ButtonGroup buttonGroup = new ButtonGroup();
	private JComboBox<String> comboBoxReader;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JButton btnFormat;
	private JButton buttonClear;
	private JButton buttonReset;
	private JButton buttonQuit;
	private JCheckBox chckbxNewCheckBox;
	private JButton btnCredit;
	private JButton btnDebit;
	private JButton btnInquire;
	private JButton btnRevoke;
	private JTextArea textArea;
	private JPanel panelCommand;
	private JPanel panelKeys;
	private JPanel panelOption;
	private JRadioButton rdbtnDES;
	private JRadioButton rdbtn3DES;

	private PcscReader readerControl = new PcscReader();
	private Acos3 acos3 = new Acos3(readerControl);
	private CardSelector cardSelector = new CardSelector(readerControl);
	private byte[] issuerCode = { 0x41, 0x43, 0x4F, 0x53, 0x54, 0x45, 0x53, 0x54 };
	private byte[] randomNum = { 0x01, 0x01, 0x01, 0x01 };
	private Charset characterSet = Charset.forName("US-ASCII");

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ACOS3Account window = new ACOS3Account();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ACOS3Account() {
		// Instantiate an event handler object
		readerControl.setEventHandler(new ReaderEvent());
		// Register the event handler implementation of this class
		readerControl.getEventHandler().addEventListener(this);
		initialize();
		resetControls();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 872, 591);
		frame.setResizable(false);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().setLayout(null);

		JLabel lblSelectReader = new JLabel("Select Reader");
		lblSelectReader.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblSelectReader.setBounds(10, 11, 87, 16);
		frame.getContentPane().add(lblSelectReader);

		comboBoxReader = new JComboBox<String>();
		comboBoxReader.setFont(new Font("Verdana", Font.PLAIN, 12));
		comboBoxReader.setEnabled(false);
		comboBoxReader.setBounds(10, 35, 417, 20);
		frame.getContentPane().add(comboBoxReader);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(259, 66, 168, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(259, 102, 168, 25);
		frame.getContentPane().add(btnConnect);

		btnFormat = new JButton("Format Card");
		btnFormat.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnFormat.setEnabled(true);
		btnFormat.setBounds(259, 138, 168, 25);
		frame.getContentPane().add(btnFormat);

		JLabel lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(437, 11, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(437, 516, 125, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(580, 516, 125, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(721, 516, 125, 25);
		frame.getContentPane().add(buttonQuit);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(437, 33, 409, 472);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setText("");
		textArea.setLineWrap(true);

		panelOption = new JPanel();
		panelOption.setBorder(
				new TitledBorder(null, "Security Option", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelOption.setBounds(10, 66, 229, 97);
		frame.getContentPane().add(panelOption);
		panelOption.setLayout(null);

		rdbtnDES = new JRadioButton("DES");
		buttonGroup.add(rdbtnDES);
		rdbtnDES.setBounds(40, 21, 109, 23);
		panelOption.add(rdbtnDES);

		rdbtn3DES = new JRadioButton("3DES");
		buttonGroup.add(rdbtn3DES);
		rdbtn3DES.setBounds(40, 56, 109, 23);
		panelOption.add(rdbtn3DES);

		panelKeys = new JPanel();
		panelKeys
				.setBorder(new TitledBorder(null, "Security Keys", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelKeys.setBounds(10, 174, 409, 164);
		frame.getContentPane().add(panelKeys);
		panelKeys.setLayout(null);

		JLabel lblDebit = new JLabel("Debit");
		lblDebit.setBounds(10, 25, 86, 14);
		panelKeys.add(lblDebit);

		JLabel lblRevoke = new JLabel("Revoke Debit");
		lblRevoke.setBounds(10, 130, 86, 14);
		panelKeys.add(lblRevoke);

		JLabel lblCertify = new JLabel("Certify");
		lblCertify.setBounds(10, 95, 86, 14);
		panelKeys.add(lblCertify);

		JLabel lblCredit = new JLabel("Credit");
		lblCredit.setBounds(10, 60, 86, 14);
		panelKeys.add(lblCredit);

		textFieldDebit = new JTextField();
		textFieldDebit.setBounds(106, 22, 293, 20);
		panelKeys.add(textFieldDebit);
		textFieldDebit.setColumns(10);

		textFieldCredit = new JTextField();
		textFieldCredit.setColumns(10);
		textFieldCredit.setBounds(106, 57, 293, 20);
		panelKeys.add(textFieldCredit);

		textFieldCertify = new JTextField();
		textFieldCertify.setColumns(10);
		textFieldCertify.setBounds(106, 92, 293, 20);
		panelKeys.add(textFieldCertify);

		textFieldRevoke = new JTextField();
		textFieldRevoke.setColumns(10);
		textFieldRevoke.setBounds(106, 127, 293, 20);
		panelKeys.add(textFieldRevoke);

		panelCommand = new JPanel();
		panelCommand.setBorder(new TitledBorder(
				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)),
				"Send Card Command", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelCommand.setBounds(10, 349, 409, 192);
		frame.getContentPane().add(panelCommand);
		panelCommand.setLayout(null);

		JLabel lblAccount = new JLabel("Account");
		lblAccount.setBounds(10, 28, 86, 14);
		panelCommand.add(lblAccount);

		textFieldAccount = new JTextField();
		textFieldAccount.setColumns(10);
		textFieldAccount.setBounds(106, 25, 293, 20);
		panelCommand.add(textFieldAccount);

		chckbxNewCheckBox = new JCheckBox("Require Debit Certificate");
		chckbxNewCheckBox.setBounds(106, 52, 240, 23);
		panelCommand.add(chckbxNewCheckBox);

		btnCredit = new JButton("Credit");
		btnCredit.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnCredit.setEnabled(true);
		btnCredit.setBounds(23, 96, 168, 25);
		panelCommand.add(btnCredit);

		btnDebit = new JButton("Debit");
		btnDebit.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnDebit.setEnabled(true);
		btnDebit.setBounds(23, 142, 168, 25);
		panelCommand.add(btnDebit);

		btnInquire = new JButton("Inquire Balance");
		btnInquire.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInquire.setEnabled(true);
		btnInquire.setBounds(212, 96, 168, 25);
		panelCommand.add(btnInquire);

		btnRevoke = new JButton("Revoke Debit");
		btnRevoke.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnRevoke.setEnabled(true);
		btnRevoke.setBounds(212, 142, 168, 25);
		panelCommand.add(btnRevoke);

		btnInitialize.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String[] readerList;
				comboBoxReader.setEnabled(true);
				comboBoxReader.removeAllItems();

				try {
					readerList = readerControl.listTerminals();

					for (int i = 0; i < readerList.length; i++)
						comboBoxReader.addItem(readerList[i]);

					if (readerList.length > 1)
						comboBoxReader.setSelectedIndex(1);

					btnConnect.setEnabled(true);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}
			}
		});

		btnConnect.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					String cardName;
					if (readerControl.isConnectionActive())
						readerControl.disconnect();

					readerControl.connect(comboBoxReader.getSelectedIndex(), "*");

					displayMessage("\r\nSuccessfully connected to "
							+ comboBoxReader.getItemAt(comboBoxReader.getSelectedIndex()));

					cardName = cardSelector.getCardNameOnRid(readerControl.getAtr());

					if (cardName == "ACOS3") {
						displayMessage("\r\nChip Type: ACOS3");
					} else {
						displayErrorMessage("Card not supported. Please use ACOS3 card.\r\n");
						return;
					}

					btnInitialize.setEnabled(false);
					btnConnect.setEnabled(false);
					buttonReset.setEnabled(true);
					comboBoxReader.setEnabled(false);

					btnFormat.setEnabled(true);
					setEnabled(panelOption, true);
					setEnabled(panelKeys, true);

				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}
			}
		});

		btnFormat.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] debitKey, creditKey, certifyKey, revokeDebitKey;
				byte len = 0;

				if (rdbtnDES.isSelected()) {
					len = 8;
					debitKey = new byte[8];
					creditKey = new byte[8];
					certifyKey = new byte[8];
					revokeDebitKey = new byte[8];
				} else if (rdbtn3DES.isSelected()) {
					len = 16;
					debitKey = new byte[16];
					creditKey = new byte[16];
					certifyKey = new byte[16];
					revokeDebitKey = new byte[16];
				}

				debitKey = textFieldDebit.getText().getBytes(characterSet);
				creditKey = textFieldCredit.getText().getBytes(characterSet);
				certifyKey = textFieldCertify.getText().getBytes(characterSet);
				revokeDebitKey = textFieldRevoke.getText().getBytes(characterSet);

				if (debitKey.length == 0)
					displayErrorMessage("Please key-in numeric value for Debit Key.");
				if (creditKey.length == 0)
					displayErrorMessage("Please key-in numeric value for Credit Key.");
				if (certifyKey.length == 0)
					displayErrorMessage("Please key-in numeric value for Certify Key.");
				if (revokeDebitKey.length == 0)
					displayErrorMessage("Please key-in numeric value for Revoke Debit Key.");
				if (creditKey.length == 0 || debitKey.length == 0 || certifyKey.length == 0
						|| revokeDebitKey.length == 0)
					return;

				if (len == 8) {
					if (creditKey.length < len || debitKey.length < len || certifyKey.length < len
							|| revokeDebitKey.length < len) {
						displayErrorMessage("Invalid input length. Length must be 8.");
						return;
					}
				} else if (len == 16) {
					if (creditKey.length < len || debitKey.length < len || certifyKey.length < len
							|| revokeDebitKey.length < len) {
						displayErrorMessage("Invalid input length. Length must be 16.");
						return;
					}
				}

				try {
					// Send IC Code
					displayMessage("\nSubmit Code - IC");
					acos3.submitCard(Acos3.CODE_TYPE.IC, issuerCode);

					// Clear card
					displayMessage("\nClear Card");
					acos3.clearCard();

					// Send IC Code
					displayMessage("\nSubmit Code - IC");
					acos3.submitCard(Acos3.CODE_TYPE.IC, issuerCode);

					// Select FF 02
					displayMessage("\nSelect File");
					acos3.selectFile(new byte[] { (byte) 0xFF, (byte) 0x02 });

					/*
					 * Write to FF 02 This will create 3 User files, no Option registers and
					 * Security Option registers defined, Personalization bit is not set
					 */
					displayMessage("\nWrite Record");
					if (rdbtnDES.isSelected())
						acos3.writeRecord((byte) 0x00, (byte) 0x00,
								new byte[] { (byte) 0x29, (byte) 0x00, (byte) 0x30, (byte) 0x00 });
					else if (rdbtn3DES.isSelected())
						acos3.writeRecord((byte) 0x00, (byte) 0x00,
								new byte[] { (byte) 0x2B, (byte) 0x00, (byte) 0x30, (byte) 0x00 });
					displayMessage("FF 02 is updated\n");

					// Send IC Code
					displayMessage("\nSubmit Code - IC");
					acos3.submitCard(Acos3.CODE_TYPE.IC, issuerCode);

					// Select user file
					displayMessage("\nSelect File");
					acos3.selectFile(new byte[] { (byte) 0xFF, (byte) 0x05 });

					// Record 00
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x00, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
					displayMessage("Record 00 is updated");

					// Record 01
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x01, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 });
					displayMessage("Record 01 is updated");

					// Record 02
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x02, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
					displayMessage("Record 02 is updated");

					// Record 03
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x03, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 });
					displayMessage("Record 03 is updated");

					// Record 04
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x04, (byte) 0x00,
							new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00 });
					displayMessage("Record 04 is updated");

					// Record 05
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x05, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
					displayMessage("Record 05 is updated");

					// Record 06
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x06, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
					displayMessage("Record 06 is updated");

					// Record 07
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x07, (byte) 0x00,
							new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
					displayMessage("Record 07 is updated");
					displayMessage("FF 05 is updated");

					// Select FF 06 file
					displayMessage("\nSelect File");
					acos3.selectFile(new byte[] { (byte) 0xFF, (byte) 0x06 });

					if (len == 8) {
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x00, (byte) 0x00, debitKey);
						displayMessage("Debit key is updated");

						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x01, (byte) 0x00, creditKey);
						displayMessage("Credit key is updated");

						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x02, (byte) 0x00, certifyKey);
						displayMessage("Certify key is updated");

						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x03, (byte) 0x00, revokeDebitKey);
						displayMessage("Revoke key is updated");
					} else if (len == 16) {
						byte[] data = new byte[8];
						System.arraycopy(debitKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x04, (byte) 0x00, data);
						displayMessage("Left Half of Debit key is updated");

						System.arraycopy(debitKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x00, (byte) 0x00, data);
						displayMessage("Right Half of Debit key is updated");

						System.arraycopy(creditKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x05, (byte) 0x00, data);
						displayMessage("Left Half of Credit key is updated");

						System.arraycopy(creditKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x01, (byte) 0x00, data);
						displayMessage("Right Half of Credit key is updated");

						System.arraycopy(certifyKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x06, (byte) 0x00, data);
						displayMessage("Left Half of Certify key is updated");

						System.arraycopy(certifyKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x02, (byte) 0x00, data);
						displayMessage("Right Half of Certify key is updated");

						System.arraycopy(revokeDebitKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x07, (byte) 0x00, data);
						displayMessage("Left Half of Revoke key is updated");

						System.arraycopy(revokeDebitKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x03, (byte) 0x00, data);
						displayMessage("Right Half of Revoke key is updated");
					}
					displayMessage("\nFF 06 is updated");

					setEnabled(panelKeys, false);
					setEnabled(panelCommand, true);
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}
			}
		});

		buttonClear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				textArea.setText("");
			}
		});

		buttonReset.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (readerControl.isConnectionActive())
						readerControl.disconnect();

					resetControls();
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		buttonQuit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (readerControl.isConnectionActive())
						readerControl.disconnect();

					System.gc();
					frame.dispose();
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnCredit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int amount;
				byte[] creditKey;
				byte[] inquireResponse;
				byte[] creditAmount = new byte[3];

				if (rdbtnDES.isSelected()) {
					creditKey = new byte[8];
				} else if (rdbtn3DES.isSelected()) {
					creditKey = new byte[16];
				}

				creditKey = textFieldCredit.getText().getBytes(characterSet);

				if (creditKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				if (textFieldAccount.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Amount.");
					return;
				}

				if (Integer.parseInt(textFieldAccount.getText()) < 1
						|| Integer.parseInt(textFieldAccount.getText()) > 16777215) {
					displayErrorMessage("Please key-in valid Amount. Valid value: 1 - 16,777,215.");
					return;
				}

				amount = Integer.parseInt(textFieldAccount.getText());

				try {
					displayMessage("\nInquire Account");
					inquireResponse = acos3.inquireAccount((byte) 0x02, randomNum);

					byte[] aMac = new byte[4];
					byte[] aAtref = new byte[6];
					byte[] aTtrefc = new byte[4];
					byte[] aData = new byte[32];

					System.arraycopy(inquireResponse, 0, aMac, 0, 4);
					System.arraycopy(inquireResponse, 8, aAtref, 0, 6);
					System.arraycopy(inquireResponse, 17, aTtrefc, 0, 4);

					creditAmount[0] = (byte) ((int) ((amount / 256) / 256) % 256);
					creditAmount[1] = (byte) ((int) (amount / 256) % 256);
					creditAmount[2] = (byte) ((int) amount % 256);

					aData[0] = (byte) 0xE2;
					aData[1] = creditAmount[0];
					aData[2] = creditAmount[1];
					aData[3] = creditAmount[2];
					System.arraycopy(aTtrefc, 0, aData, 4, 4);
					// Increment the last 2 Bytes of the ATREF. ATREF is composed of 4-byte AID and
					// 2-byte ATC.
					long tmpVal = (int) ((aAtref[4] * 256) + aAtref[5]) + 1;
					aAtref[4] = (byte) (tmpVal / 256);
					aAtref[5] = (byte) (tmpVal % 256);
					System.arraycopy(aAtref, 0, aData, 8, 6);
					aData[14] = 0x00;
					aData[15] = 0x00;

					// Format Credit command data and execute credit command
					// Using tmpArray, the first four bytes are carried over
					if (rdbtnDES.isSelected()) {
						Chain3DES.singleMac(aData, creditKey);
						System.arraycopy(aData, 0, aMac, 0, 4);
					} else if (rdbtn3DES.isSelected()) {
						byte[] mac = new byte[4];
						mac = Chain3DES.tripleMac(aData, creditKey);
						System.arraycopy(mac, 0, aMac, 0, 4);
					}

					displayMessage("\nCredit");
					acos3.credit(aMac, creditAmount, aTtrefc);
					displayMessage("Credit transaction completed\n");
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnDebit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int amount;
				byte[] DebitKey;
				byte[] inquireResponse;
				byte[] creditAmount = new byte[3];

				if (rdbtnDES.isSelected()) {
					DebitKey = new byte[8];
				} else if (rdbtn3DES.isSelected()) {
					DebitKey = new byte[16];
				}

				DebitKey = textFieldDebit.getText().getBytes(characterSet);

				if (DebitKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				if (textFieldAccount.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Amount.");
					return;
				}

				if (Integer.parseInt(textFieldAccount.getText()) < 1
						|| Integer.parseInt(textFieldAccount.getText()) > 16777215) {
					displayErrorMessage("Please key-in valid Amount. Valid value: 1 - 16,777,215.");
					return;
				}

				amount = Integer.parseInt(textFieldAccount.getText());

				try {
					displayMessage("\nInquire Account");
					inquireResponse = acos3.inquireAccount((byte) 0x02, randomNum);

					byte[] aMac = new byte[4];
					byte[] aAtref = new byte[6];
					byte[] aTtrefd = new byte[4];
					byte[] aData = new byte[32];
					byte[] aDebitResponse = null;

					System.arraycopy(inquireResponse, 0, aMac, 0, 4);
					System.arraycopy(inquireResponse, 8, aAtref, 0, 6);
					System.arraycopy(inquireResponse, 21, aTtrefd, 0, 4);

					creditAmount[0] = (byte) ((int) ((amount / 256) / 256) % 256);
					creditAmount[1] = (byte) ((int) (amount / 256) % 256);
					creditAmount[2] = (byte) ((int) amount % 256);

					aData[0] = (byte) 0xE6;
					aData[1] = creditAmount[0];
					aData[2] = creditAmount[1];
					aData[3] = creditAmount[2];
					System.arraycopy(aTtrefd, 0, aData, 4, 4);
					// Increment the last 2 Bytes of the ATREF. ATREF is composed of 4-byte AID and
					// 2-byte ATC.
					long tmpVal = (int) ((aAtref[4] * 256) + aAtref[5]) + 1;
					aAtref[4] = (byte) (tmpVal / 256);
					aAtref[5] = (byte) (tmpVal % 256);
					System.arraycopy(aAtref, 0, aData, 8, 6);
					aData[14] = 0x00;
					aData[15] = 0x00;

					// Format Credit command data and execute credit command
					// Using tmpArray, the first four bytes are carried over
					if (rdbtnDES.isSelected()) {
						Chain3DES.singleMac(aData, DebitKey);
						System.arraycopy(aData, 0, aMac, 0, 4);
					} else if (rdbtn3DES.isSelected()) {
						byte[] mac = new byte[4];
						mac = Chain3DES.tripleMac(aData, DebitKey);
						System.arraycopy(mac, 0, aMac, 0, 4);
					}

					displayMessage("\nDebit");
					if (chckbxNewCheckBox.isSelected()) {
						aDebitResponse = acos3.debit(aMac, creditAmount, aTtrefd, (byte) 0x01);
						displayMessage("DEBIT CERTIFICATE: " + Helper.byteAsString(aDebitResponse, true));
					} else {
						acos3.debit(aMac, creditAmount, aTtrefd, (byte) 0x00);
					}

					displayMessage("Debit transaction completed\n");
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnInquire.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] certifyKey;
				byte[] inquireResponse;

				if (rdbtnDES.isSelected()) {
					certifyKey = new byte[8];
				} else if (rdbtn3DES.isSelected()) {
					certifyKey = new byte[16];
				}

				certifyKey = textFieldCertify.getText().getBytes(characterSet);

				if (certifyKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				try {
					displayMessage("\nInquire Account");
					inquireResponse = acos3.inquireAccount((byte) 0x02, randomNum);

					byte[] aMac = new byte[4];
					byte[] aAtref = new byte[6];
					byte[] aTtrefc = new byte[4];
					byte[] aTtrefd = new byte[4];
					byte[] balanceAmount = new byte[4];
					byte[] aData = new byte[32];

					System.arraycopy(inquireResponse, 0, aMac, 0, 4);
					System.arraycopy(inquireResponse, 5, balanceAmount, 1, 3);
					System.arraycopy(inquireResponse, 8, aAtref, 0, 6);
					System.arraycopy(inquireResponse, 17, aTtrefc, 0, 4);
					System.arraycopy(inquireResponse, 21, aTtrefd, 0, 4);

					System.arraycopy(randomNum, 0, aData, 0, 4);
					aData[4] = inquireResponse[4];
					System.arraycopy(balanceAmount, 1, aData, 5, 3);
					System.arraycopy(aAtref, 0, aData, 8, 6);
					aData[14] = 0x00;
					aData[15] = 0x00;
					System.arraycopy(aTtrefc, 0, aData, 16, 4);
					System.arraycopy(aTtrefd, 0, aData, 20, 4);

					int amount = Helper.byteToInt(balanceAmount);

					// Format Credit command data and execute credit command
					// Using tmpArray, the first four bytes are carried over
					if (rdbtnDES.isSelected()) {
						Chain3DES.singleMac(aData, certifyKey);
						System.arraycopy(aData, 0, aMac, 0, 4);

						// Compare MAC values
						for (int indx = 0; indx < 4; indx++) {
							if (aData[indx] != aMac[indx]) {
								displayMessage("MAC is incorrect, data integrity is jeopardized.");
								return;
							}
						}
					} else if (rdbtn3DES.isSelected()) {
						byte[] mac = new byte[4];
						mac = Chain3DES.tripleMac(aData, certifyKey);

						// Compare MAC values
						for (int indx = 0; indx < 4; indx++) {
							if (mac[indx] != aMac[indx]) {
								displayMessage("MAC is incorrect, data integrity is jeopardized.");
								return;
							}
						}
					}

					// Display relevant data from ACOS card
					switch (inquireResponse[4]) {
					case 1:
						displayMessage("Last transaction is DEBIT");
						break;
					case 2:
						displayMessage("Last transaction is REVOKE DEBIT");
						break;
					case 3:
						displayMessage("Last transaction is CREDIT");
						break;
					default:
						displayMessage("Last transaction is NOT DEFINED");
						break;
					}

					displayMessage("Amount is " + Integer.toString(amount));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnRevoke.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int amount;
				byte[] revokeDebitKey;
				byte[] inquireResponse;
				byte[] creditAmount = new byte[3];

				if (rdbtnDES.isSelected()) {
					revokeDebitKey = new byte[8];
				} else if (rdbtn3DES.isSelected()) {
					revokeDebitKey = new byte[16];
				}

				revokeDebitKey = textFieldRevoke.getText().getBytes(characterSet);

				if (revokeDebitKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				if (textFieldAccount.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Amount.");
					return;
				}

				if (Integer.parseInt(textFieldAccount.getText()) < 1
						|| Integer.parseInt(textFieldAccount.getText()) > 16777215) {
					displayErrorMessage("Please key-in valid Amount. Valid value: 1 - 16,777,215.");
					return;
				}

				amount = Integer.parseInt(textFieldAccount.getText());

				try {
					displayMessage("\nInquire Account");
					inquireResponse = acos3.inquireAccount((byte) 0x02, randomNum);

					byte[] aMac = new byte[4];
					byte[] aAtref = new byte[6];
					byte[] aTtrefd = new byte[4];
					byte[] aData = new byte[32];

					System.arraycopy(inquireResponse, 0, aMac, 0, 4);
					System.arraycopy(inquireResponse, 8, aAtref, 0, 6);
					System.arraycopy(inquireResponse, 21, aTtrefd, 0, 4);

					creditAmount[0] = (byte) ((int) ((amount / 256) / 256) % 256);
					creditAmount[1] = (byte) ((int) (amount / 256) % 256);
					creditAmount[2] = (byte) ((int) amount % 256);

					aData[0] = (byte) 0xE8;
					aData[1] = creditAmount[0];
					aData[2] = creditAmount[1];
					aData[3] = creditAmount[2];
					System.arraycopy(aTtrefd, 0, aData, 4, 4);
					// Increment the last 2 Bytes of the ATREF. ATREF is composed of 4-byte AID and
					// 2-byte ATC.
					long tmpVal = (int) ((aAtref[4] * 256) + aAtref[5]) + 1;
					aAtref[4] = (byte) (tmpVal / 256);
					aAtref[5] = (byte) (tmpVal % 256);
					System.arraycopy(aAtref, 0, aData, 8, 6);
					aData[14] = 0x00;
					aData[15] = 0x00;

					// Format Credit command data and execute credit command
					// Using tmpArray, the first four bytes are carried over
					if (rdbtnDES.isSelected()) {
						Chain3DES.singleMac(aData, revokeDebitKey);
						System.arraycopy(aData, 0, aMac, 0, 4);
					} else if (rdbtn3DES.isSelected()) {
						byte[] mac = new byte[4];
						mac = Chain3DES.tripleMac(aData, revokeDebitKey);
						System.arraycopy(mac, 0, aMac, 0, 4);
					}

					displayMessage("Revoke Debit");
					acos3.revokeDebit(aMac);
					displayMessage("Revoke Debit transaction completed\n");
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		rdbtnDES.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				textFieldDebit.setDocument(new JTextFieldLimit(8));
				textFieldCredit.setDocument(new JTextFieldLimit(8));
				textFieldCertify.setDocument(new JTextFieldLimit(8));
				textFieldRevoke.setDocument(new JTextFieldLimit(8));
				if (cb.isSelected()) {
					if (textFieldDebit.getText().length() >= 8) {
						textFieldDebit.setText("");
					}
					if (textFieldCredit.getText().length() >= 8) {
						textFieldCredit.setText("");
					}
					if (textFieldCertify.getText().length() >= 8) {
						textFieldCertify.setText("");
					}
					if (textFieldRevoke.getText().length() >= 8) {
						textFieldRevoke.setText("");
					}
				}
			}
		});

		rdbtn3DES.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				textFieldDebit.setDocument(new JTextFieldLimit(16));
				textFieldCredit.setDocument(new JTextFieldLimit(16));
				textFieldCertify.setDocument(new JTextFieldLimit(16));
				textFieldRevoke.setDocument(new JTextFieldLimit(16));
				if (cb.isSelected()) {
					if (textFieldDebit.getText().length() >= 16) {
						textFieldDebit.setText("");
					}
					if (textFieldCredit.getText().length() >= 16) {
						textFieldCredit.setText("");
					}
					if (textFieldCertify.getText().length() >= 16) {
						textFieldCertify.setText("");
					}
					if (textFieldRevoke.getText().length() >= 16) {
						textFieldRevoke.setText("");
					}
				}
			}
		});
	}

	void resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		btnFormat.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);

		setEnabled(panelOption, false);
		setEnabled(panelKeys, false);
		setEnabled(panelCommand, false);

		textFieldDebit.setText("");
		textFieldCredit.setText("");
		textFieldCertify.setText("");
		textFieldRevoke.setText("");
		textFieldAccount.setText("");

		displayMessage("Program Ready");
	}

	void setEnabled(Component component, boolean enable) {
		component.setEnabled(enable);
		// Check for instance of container.
		if (component instanceof Container) {
			// Continue setEnabled if there are more components.
			Component components[] = ((Container) component).getComponents();
			for (int i = 0; i < components.length; i++) {
				if (components[i] != null) {
					setEnabled(components[i], enable); // <- Here is the recursive call.
				}
			}
		}
	}

	void displayMessage(String message) {
		textArea.append(message + "\r\n");
	}

	void displayErrorMessage(String message) {
		JOptionPane.showMessageDialog(null, message, "Failed", JOptionPane.ERROR_MESSAGE);
		displayMessage(message);
	}

	@Override
	public void onSendCommand(TransmitApduEventArg event) {
		// TODO Auto-generated method stub
		displayMessage("<< " + event.getAsString(true));
	}

	@Override
	public void onReceiveCommand(TransmitApduEventArg event) {
		// TODO Auto-generated method stub
		displayMessage(">> " + event.getAsString(true));
	}
}
