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.JPanel;
import javax.swing.border.TitledBorder;

import Acos.Acos3;
import Acos.Acos3.CODE_TYPE;
import AcsCommon.Chain3DES;
import Pcsc.CardSelector;
import Pcsc.PcscReader;
import Pcsc.ReaderEvent;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.JRadioButton;
import javax.swing.JButton;
import javax.swing.border.EtchedBorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;

import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;
import java.awt.event.ActionEvent;

import javax.smartcardio.CardException;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;

public class ACOS3SecretCodesEncryption extends JFrame implements ReaderEvent.TransmitApduHandler {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textFieldCardKey;
	private JTextField textFieldTerminalKey;
	private JTextField textFieldValue;
	private JComboBox<String> comboBoxReader;
	private JRadioButton rdbtnDES;
	private JRadioButton rdbtn3DES;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JPanel panelKeyTemplate;
	private JButton btnFormatCard;
	private JButton buttonClear;
	private JButton buttonReset;
	private JButton buttonQuit;
	private JTextArea textArea;
	private JRadioButton rdbtnNotEncryp;
	private JRadioButton rdbtnEncrypted;
	private JPanel panelCodeSubmission;
	private JPanel panelEncryption;
	private JButton btnSubmit;
	private JButton btnSetValue;
	private JComboBox<String> comboBoxCode;
	private JPanel panelSecurity;
	private final ButtonGroup buttonGroupSecurity = new ButtonGroup();
	private final ButtonGroup buttonGroupEncryp = new ButtonGroup();

	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 Charset characterSet = Charset.forName("US-ASCII");

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ACOS3SecretCodesEncryption window = new ACOS3SecretCodesEncryption();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ACOS3SecretCodesEncryption() {
		// 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, 738, 645);
		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, 335, 20);
		frame.getContentPane().add(comboBoxReader);

		panelSecurity = new JPanel();
		panelSecurity.setLayout(null);
		panelSecurity.setEnabled(false);
		panelSecurity.setBorder(
				new TitledBorder(null, "Security Option", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelSecurity.setBounds(213, 102, 132, 97);
		frame.getContentPane().add(panelSecurity);

		rdbtnDES = new JRadioButton("DES");
		buttonGroupSecurity.add(rdbtnDES);
		rdbtnDES.setBounds(40, 21, 75, 23);
		rdbtnDES.setSelected(true);
		panelSecurity.add(rdbtnDES);

		rdbtn3DES = new JRadioButton("3DES");
		buttonGroupSecurity.add(rdbtn3DES);
		rdbtn3DES.setBounds(40, 56, 75, 23);
		panelSecurity.add(rdbtn3DES);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(10, 66, 145, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(200, 66, 145, 25);
		frame.getContentPane().add(btnConnect);

		panelKeyTemplate = new JPanel();
		panelKeyTemplate.setLayout(null);
		panelKeyTemplate.setEnabled(false);
		panelKeyTemplate.setBorder(new TitledBorder(

				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)),

				"Key Template", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelKeyTemplate.setBounds(10, 210, 335, 175);
		frame.getContentPane().add(panelKeyTemplate);

		btnFormatCard = new JButton("Format Card");
		btnFormatCard.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnFormatCard.setEnabled(true);
		btnFormatCard.setBounds(157, 138, 168, 25);
		panelKeyTemplate.add(btnFormatCard);

		JLabel lblCard = new JLabel("Card Key");
		lblCard.setBounds(10, 26, 122, 14);
		panelKeyTemplate.add(lblCard);

		textFieldCardKey = new JTextField();
		textFieldCardKey.setText("");
		textFieldCardKey.setColumns(10);
		textFieldCardKey.setBounds(10, 51, 315, 20);
		panelKeyTemplate.add(textFieldCardKey);

		textFieldTerminalKey = new JTextField();
		textFieldTerminalKey.setText("");
		textFieldTerminalKey.setColumns(10);
		textFieldTerminalKey.setBounds(10, 107, 315, 20);
		panelKeyTemplate.add(textFieldTerminalKey);

		JLabel lblTerminal = new JLabel("Terminal Key");
		lblTerminal.setBounds(10, 82, 122, 14);
		panelKeyTemplate.add(lblTerminal);

		JLabel lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(355, 11, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(355, 570, 105, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(482, 570, 105, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(607, 570, 105, 25);
		frame.getContentPane().add(buttonQuit);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(355, 33, 357, 526);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setText("");
		textArea.setLineWrap(true);

		panelEncryption = new JPanel();
		panelEncryption.setLayout(null);
		panelEncryption.setEnabled(false);
		panelEncryption.setBorder(new TitledBorder(
				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)),
				"Encryption Option", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelEncryption.setBounds(10, 102, 173, 97);
		frame.getContentPane().add(panelEncryption);

		rdbtnNotEncryp = new JRadioButton("Not Encrypted");
		buttonGroupEncryp.add(rdbtnNotEncryp);
		rdbtnNotEncryp.setBounds(40, 21, 110, 23);
		rdbtnNotEncryp.setSelected(true);
		panelEncryption.add(rdbtnNotEncryp);

		rdbtnEncrypted = new JRadioButton("Encrypted");
		buttonGroupEncryp.add(rdbtnEncrypted);
		rdbtnEncrypted.setBounds(40, 56, 110, 23);
		panelEncryption.add(rdbtnEncrypted);

		panelCodeSubmission = new JPanel();
		panelCodeSubmission.setLayout(null);
		panelCodeSubmission.setEnabled(false);
		panelCodeSubmission.setBorder(new TitledBorder(
				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)),
				"Code Submission", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelCodeSubmission.setBounds(10, 396, 335, 199);
		frame.getContentPane().add(panelCodeSubmission);

		btnSubmit = new JButton("Submit");
		btnSubmit.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnSubmit.setEnabled(true);
		btnSubmit.setBounds(190, 150, 135, 25);
		panelCodeSubmission.add(btnSubmit);

		JLabel lblCode = new JLabel("Code");
		lblCode.setBounds(10, 26, 122, 14);
		panelCodeSubmission.add(lblCode);

		textFieldValue = new JTextField();
		textFieldValue.setText("");
		textFieldValue.setColumns(10);
		textFieldValue.setDocument(new JTextFieldLimit(8, true));
		textFieldValue.setBounds(10, 107, 315, 20);
		panelCodeSubmission.add(textFieldValue);

		JLabel lblValue = new JLabel("Value");
		lblValue.setBounds(10, 82, 122, 14);
		panelCodeSubmission.add(lblValue);

		btnSetValue = new JButton("Set Value");
		btnSetValue.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnSetValue.setEnabled(true);
		btnSetValue.setBounds(10, 150, 135, 25);
		panelCodeSubmission.add(btnSetValue);

		comboBoxCode = new JComboBox<String>();
		comboBoxCode.setModel(new DefaultComboBoxModel<String>(new String[] { "PIN", "Application Code 1", "Application Code 2",
				"Application Code 3", "Application Code 4", "Application Code 5" }));
		comboBoxCode.setFont(new Font("Verdana", Font.PLAIN, 12));
		comboBoxCode.setEnabled(false);
		comboBoxCode.setBounds(10, 51, 315, 20);
		panelCodeSubmission.add(comboBoxCode);

		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);

					setEnabled(panelSecurity, true);
					setEnabled(panelEncryption, true);
					setEnabled(panelKeyTemplate, true);

				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}
			}
		});

		btnFormatCard.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] aCardKey, aTerminalKey;
				byte[] aData = new byte[] { 0x00, 0x00, 0x30, 0x00 };

				aCardKey = textFieldCardKey.getText().getBytes(characterSet);
				aTerminalKey = textFieldTerminalKey.getText().getBytes(characterSet);

				if (aCardKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Debit Key.");
					return;
				}
				if (aTerminalKey.length == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				if (rdbtnDES.isSelected()) {
					if (aCardKey.length < 8 || aTerminalKey.length < 8) {
						displayErrorMessage("Invalid input length. Length must be 8.");
						return;
					}
				} else if (rdbtn3DES.isSelected()) {
					if (aCardKey.length < 16 || aTerminalKey.length < 16) {
						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())
						aData[0] = 0x00;
					else if (rdbtn3DES.isSelected())
						aData[0] = 0x02;

					if (rdbtnNotEncryp.isSelected())
						aData[1] = 0x00;
					else if (rdbtnEncrypted.isSelected())
						aData[1] = 0x7E;

					acos3.writeRecord((byte) 0x00, (byte) 0x00, aData);
					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) 0x03 });

					if (rdbtnDES.isSelected()) {

						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x02, (byte) 0x00, aCardKey);
						displayMessage("Card key is updated");

						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x03, (byte) 0x00, aTerminalKey);
						displayMessage("Terminal key is updated");

					} else if (rdbtn3DES.isSelected()) {

						byte[] data = new byte[8];
						System.arraycopy(aCardKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x02, (byte) 0x00, data);
						displayMessage("Left Half of Debit key is updated");

						System.arraycopy(aCardKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x0C, (byte) 0x00, data);
						displayMessage("Right Half of Debit key is updated");

						System.arraycopy(aTerminalKey, 0, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x03, (byte) 0x00, data);
						displayMessage("Left Half of Credit key is updated");

						System.arraycopy(aTerminalKey, 8, data, 0, 8);
						displayMessage("\nWrite Record");
						acos3.writeRecord((byte) 0x0D, (byte) 0x00, data);
						displayMessage("Right Half of Credit key is updated");

					}

					setEnabled(panelSecurity, false);
					setEnabled(panelEncryption, false);
					setEnabled(panelKeyTemplate, false);
					setEnabled(panelCodeSubmission, true);
					btnSubmit.setEnabled(false);

				} 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());
				}
			}
		});

		btnSubmit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				CODE_TYPE codeType = CODE_TYPE.IC;
				byte[] codeValue = new byte[8];
				byte[] aTemp = new byte[8];
				byte[] aEncryptedKey = new byte[8];

				if (textFieldValue.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				codeValue = textFieldValue.getText().getBytes(characterSet);

				switch (comboBoxCode.getSelectedIndex()) {
				case 0:
					codeType = CODE_TYPE.PIN;
					break;
				case 1:
					codeType = CODE_TYPE.AC1;
					break;
				case 2:
					codeType = CODE_TYPE.AC2;
					break;
				case 3:
					codeType = CODE_TYPE.AC3;
					break;
				case 4:
					codeType = CODE_TYPE.AC4;
					break;
				case 5:
					codeType = CODE_TYPE.AC5;
					break;
				default:
					displayErrorMessage("Invalid Code Type");
					break;
				}

				try {
					if (rdbtnNotEncryp.isSelected())
						System.arraycopy(codeValue, 0, aEncryptedKey, 0, 8);
					else {
						if (rdbtnDES.isSelected()) {
							Chain3DES.des(codeValue, getSessionKey());
							System.arraycopy(codeValue, 0, aEncryptedKey, 0, 8);
						} else if (rdbtn3DES.isSelected()) {
							aTemp = Chain3DES.tripleDes(codeValue, getSessionKey());
							System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);
						}
					}

					// Submit Code
					displayMessage("\nSubmit Code");
					acos3.submitCard(codeType, aEncryptedKey);

				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnSetValue.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte codeType = 0x00;
				byte[] codeValue = new byte[8];

				if (textFieldValue.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Credit Key.");
					return;
				}

				codeValue = textFieldValue.getText().getBytes(characterSet);

				switch (comboBoxCode.getSelectedIndex()) {
				case 0:
					codeType = 0x01;
					break;
				case 1:
					codeType = 0x05;
					break;
				case 2:
					codeType = 0x06;
					break;
				case 3:
					codeType = 0x07;
					break;
				case 4:
					codeType = 0x08;
					break;
				case 5:
					codeType = 0x09;
					break;
				default:
					displayErrorMessage("Invalid Code Type");
					break;
				}

				try {
					// 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) 0x03 });

					displayMessage("\nWrite Record");
					acos3.writeRecord(codeType, (byte) 0x00, codeValue);
					displayMessage("Write left half of card key OK");

					displayMessage("\n" + textFieldValue.getText() + " set successfully");

					btnSubmit.setEnabled(true);

				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		rdbtnDES.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				textFieldCardKey.setDocument(new JTextFieldLimit(8));
				textFieldTerminalKey.setDocument(new JTextFieldLimit(8));

				if (cb.isSelected()) {
					if (textFieldCardKey.getText().length() >= 8) {
						textFieldCardKey.setText("");
					}
					if (textFieldTerminalKey.getText().length() >= 8) {
						textFieldTerminalKey.setText("");
					}
					rdbtn3DES.setSelected(false);
				}
			}
		});

		rdbtn3DES.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				textFieldCardKey.setDocument(new JTextFieldLimit(16));
				textFieldTerminalKey.setDocument(new JTextFieldLimit(16));

				if (cb.isSelected()) {
					if (textFieldCardKey.getText().length() >= 16) {
						textFieldCardKey.setText("");
					}
					if (textFieldTerminalKey.getText().length() >= 16) {
						textFieldTerminalKey.setText("");
					}
					rdbtnDES.setSelected(false);
				}
			}
		});
		
		rdbtnNotEncryp.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				if (cb.isSelected())
					rdbtnEncrypted.setSelected(false);
			}
		});

		rdbtnEncrypted.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				JRadioButton cb = (JRadioButton) event.getSource();
				if (cb.isSelected())
					rdbtnNotEncryp.setSelected(false);
			}
		});
	}

	void resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);

		setEnabled(panelSecurity, false);
		setEnabled(panelEncryption, false);
		setEnabled(panelKeyTemplate, false);
		setEnabled(panelCodeSubmission, false);

		textFieldCardKey.setText("");
		textFieldTerminalKey.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));
	}

	private byte[] getSessionKey() throws Exception {
		byte[] aSessionKey = null;
		byte[] aTemp = new byte[8];
		byte[] aCardRandomNum = new byte[8];
		byte[] aEncryptedKey = new byte[8];
		byte[] aTerminalRandomNum = new byte[8];
		byte[] aCardKey, aTerminalKey, aReverseKey;

		aCardKey = textFieldCardKey.getText().getBytes(characterSet);
		aTerminalKey = textFieldTerminalKey.getText().getBytes(characterSet);

		displayMessage("\nStart Session");
		aCardRandomNum = acos3.startSession();

		// Encrypt the Card Random Number with Terminal Key
		if (rdbtnDES.isSelected()) {
			System.arraycopy(aCardRandomNum, 0, aTemp, 0, 8);
			Chain3DES.des(aTemp, aTerminalKey);
			System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);
		} else if (rdbtn3DES.isSelected()) {
			aTemp = Chain3DES.tripleDes(aCardRandomNum, aTerminalKey);
			System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);
		}

		acos3.authenticate(aEncryptedKey, aTerminalRandomNum);

		// Compute for Session Key
		if (rdbtnDES.isSelected()) {

			aSessionKey = new byte[8];

			System.arraycopy(aCardRandomNum, 0, aTemp, 0, 8);
			Chain3DES.des(aTemp, aCardKey);
			System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);

			// XOR the result with tRnd
			for (int index = 0; index < 8; index++)
				aEncryptedKey[index] = (byte) (aEncryptedKey[index] ^ (byte) aTerminalRandomNum[index]);

			Chain3DES.des(aEncryptedKey, aTerminalKey);
			System.arraycopy(aEncryptedKey, 0, aSessionKey, 0, 8);

		} else if (rdbtn3DES.isSelected()) {

			aSessionKey = new byte[16];
			aReverseKey = new byte[16];

			aTemp = Chain3DES.tripleDes(aCardRandomNum, aCardKey);
			System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);

			aTemp = Chain3DES.tripleDes(aEncryptedKey, aTerminalKey);
			System.arraycopy(aTemp, 0, aEncryptedKey, 0, 8);

			System.arraycopy(aEncryptedKey, 0, aSessionKey, 0, 8);
			System.arraycopy(aTerminalKey, 8, aReverseKey, 0, 8);
			System.arraycopy(aTerminalKey, 0, aReverseKey, 8, 8);

			aTemp = Chain3DES.tripleDes(aTerminalRandomNum, aReverseKey);
			System.arraycopy(aTemp, 0, aSessionKey, 8, 8);
		}

		return aSessionKey;
	}
}
