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.smartcardio.CardException;
import javax.swing.JButton;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
import javax.swing.text.MaskFormatter;

import AcsCommon.Helper;
import Pcsc.CardSelector;
import Pcsc.PcscReader;
import Pcsc.ReaderEvent;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.JFormattedTextField;
import javax.swing.JRadioButton;
import javax.swing.border.EtchedBorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.awt.event.ActionEvent;
import javax.swing.ButtonGroup;

public class MifareChangeKey extends JFrame implements ReaderEvent.TransmitApduHandler {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textFieldKeyStore;
	private JTextField textFieldSector;
	private JTextField textFieldKeyStoreNumber;
	private JTextField textFieldCurrent;
	private JTextField textFieldBlock;
	private JComboBox<String> comboBoxReader;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JButton buttonClear;
	private JButton buttonReset;
	private JButton buttonQuit;
	private JTextArea textArea;
	private JButton btnLoadKey;
	private JFormattedTextField formattedTextFieldValueInput;
	private JRadioButton rdbtnKeyA;
	private JRadioButton rdbtnKeyB;
	private JButton btnAuthenticate;
	private JFormattedTextField formattedTextFieldKeyA;
	private JFormattedTextField formattedTextFieldKeyB;
	private JFormattedTextField formattedTextFieldAccessBits;
	private JButton btnRead;
	private JButton btnUpdate;

	private PcscReader readerControl = new PcscReader();
	private ReaderFunctions readerFunctions = new ReaderFunctions(readerControl);
	private MifareClassic mifareClassic = new MifareClassic(readerControl);
	private CardSelector cardSelector = new CardSelector(readerControl);
	private MaskFormatter mask;
	private MaskFormatter mask1;
	private byte maximumSector;
	private String cardName;
	private byte currentSector, currentSectorTrailer;

	final static public String HEX_FORMATTER_FOUR_BYTES = "HH HH HH HH";
	final static public String HEX_FORMATTER_SIX_BYTES = "HH HH HH HH HH HH";
	private JPanel panel;
	private JPanel panelFunction;
	private JPanel panelChange;
	private final ButtonGroup buttonGroup = new ButtonGroup();

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MifareChangeKey window = new MifareChangeKey();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public MifareChangeKey() {
		// 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, 914, 668);
		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, 13, 87, 16);
		frame.getContentPane().add(lblSelectReader);

		comboBoxReader = new JComboBox<String>();
		comboBoxReader.setFont(new Font("Verdana", Font.PLAIN, 12));
		comboBoxReader.setEnabled(false);
		comboBoxReader.setBounds(107, 11, 331, 20);
		frame.getContentPane().add(comboBoxReader);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(270, 42, 168, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(270, 69, 168, 25);
		frame.getContentPane().add(btnConnect);

		JLabel lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(448, 13, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(448, 593, 140, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(598, 593, 140, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(748, 593, 140, 25);
		frame.getContentPane().add(buttonQuit);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(448, 33, 440, 549);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setText("");
		textArea.setLineWrap(true);

		panel = new JPanel();
		panel.setBorder(new TitledBorder(null, "Store Authentication Keys to Device", TitledBorder.LEADING,
				TitledBorder.TOP, null, null));
		panel.setBounds(10, 105, 428, 124);
		frame.getContentPane().add(panel);
		panel.setLayout(null);

		btnLoadKey = new JButton("Load Key");
		btnLoadKey.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnLoadKey.setEnabled(true);
		btnLoadKey.setBounds(250, 88, 168, 25);
		panel.add(btnLoadKey);

		textFieldKeyStore = new JTextField();
		textFieldKeyStore.setDocument(new JTextFieldLimit(2, true));
		textFieldKeyStore.setText("");
		textFieldKeyStore.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldKeyStore.setColumns(2);
		textFieldKeyStore.setBounds(192, 26, 40, 20);
		panel.add(textFieldKeyStore);

		JLabel lblKeyStoreNumber = new JLabel("Key Store Number (Hex)");
		lblKeyStoreNumber.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblKeyStoreNumber.setBounds(10, 27, 172, 16);
		panel.add(lblKeyStoreNumber);

		mask = null;
		try {
			mask = new MaskFormatter(HEX_FORMATTER_SIX_BYTES);
		} catch (ParseException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		mask.setPlaceholderCharacter('_');
		formattedTextFieldValueInput = new JFormattedTextField(mask);
		formattedTextFieldValueInput.setHorizontalAlignment(SwingConstants.CENTER);
		formattedTextFieldValueInput.setBounds(192, 57, 160, 25);
		panel.add(formattedTextFieldValueInput);

		JLabel lblKeyValueInput = new JLabel("Key Value Input");
		lblKeyValueInput.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblKeyValueInput.setBounds(10, 60, 172, 16);
		panel.add(lblKeyValueInput);

		panelFunction = new JPanel();
		panelFunction.setBorder(
				new TitledBorder(null, "Authentication Function", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelFunction.setBounds(10, 240, 428, 187);
		frame.getContentPane().add(panelFunction);
		panelFunction.setLayout(null);

		JPanel panelKeyType = new JPanel();
		panelKeyType.setLayout(null);
		panelKeyType.setBorder(new TitledBorder(
				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)), "Key Type:",
				TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelKeyType.setBounds(10, 22, 172, 61);
		panelFunction.add(panelKeyType);

		rdbtnKeyA = new JRadioButton("KeyA");
		buttonGroup.add(rdbtnKeyA);
		rdbtnKeyA.setBounds(6, 25, 66, 23);
		panelKeyType.add(rdbtnKeyA);

		rdbtnKeyB = new JRadioButton("KeyB");
		buttonGroup.add(rdbtnKeyB);
		rdbtnKeyB.setBounds(74, 25, 66, 23);
		panelKeyType.add(rdbtnKeyB);

		textFieldSector = new JTextField();
		textFieldSector.setText("");
		textFieldSector.setDocument(new JTextFieldLimit(2, true));
		textFieldSector.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldSector.setColumns(2);
		textFieldSector.setBounds(192, 90, 40, 20);
		panelFunction.add(textFieldSector);

		JLabel lblSectorNumber = new JLabel("Sector Number (Hex)");
		lblSectorNumber.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblSectorNumber.setBounds(10, 91, 172, 16);
		panelFunction.add(lblSectorNumber);

		textFieldKeyStoreNumber = new JTextField();
		textFieldKeyStoreNumber.setText("");
		textFieldKeyStoreNumber.setDocument(new JTextFieldLimit(2, true));
		textFieldKeyStoreNumber.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldKeyStoreNumber.setColumns(2);
		textFieldKeyStoreNumber.setBounds(192, 121, 40, 20);
		panelFunction.add(textFieldKeyStoreNumber);

		JLabel lblKeyStoreNumberHex = new JLabel("Key Store Number (Hex)");
		lblKeyStoreNumberHex.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblKeyStoreNumberHex.setBounds(10, 122, 172, 16);
		panelFunction.add(lblKeyStoreNumberHex);

		JLabel lbl39 = new JLabel("00 - 27 hex");
		lbl39.setFont(new Font("Verdana", Font.PLAIN, 12));
		lbl39.setBounds(242, 93, 172, 16);
		panelFunction.add(lbl39);

		JLabel lbl1 = new JLabel("00 - 01");
		lbl1.setFont(new Font("Verdana", Font.PLAIN, 12));
		lbl1.setBounds(242, 124, 172, 16);
		panelFunction.add(lbl1);

		btnAuthenticate = new JButton("Authenticate");
		btnAuthenticate.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnAuthenticate.setEnabled(true);
		btnAuthenticate.setBounds(252, 151, 168, 25);
		panelFunction.add(btnAuthenticate);

		panelChange = new JPanel();
		panelChange.setBorder(
				new TitledBorder(null, "Change Sector Keys", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelChange.setBounds(10, 438, 428, 180);
		frame.getContentPane().add(panelChange);
		panelChange.setLayout(null);

		textFieldCurrent = new JTextField();
		textFieldCurrent.setDocument(new JTextFieldLimit(2, true));
		textFieldCurrent.setText("");
		textFieldCurrent.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldCurrent.setColumns(2);
		textFieldCurrent.setBounds(192, 22, 40, 20);
		panelChange.add(textFieldCurrent);

		JLabel lblCurrentSector = new JLabel("Current Sector (Hex)");
		lblCurrentSector.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblCurrentSector.setBounds(10, 23, 172, 16);
		panelChange.add(lblCurrentSector);

		textFieldBlock = new JTextField();
		textFieldBlock.setDocument(new JTextFieldLimit(2, true));
		textFieldBlock.setText("");
		textFieldBlock.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldBlock.setColumns(2);
		textFieldBlock.setBounds(192, 53, 40, 20);
		panelChange.add(textFieldBlock);

		JLabel lblSectorTrailhex = new JLabel("Sector Trailer Block (Hex)");
		lblSectorTrailhex.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblSectorTrailhex.setBounds(10, 54, 172, 16);
		panelChange.add(lblSectorTrailhex);

		formattedTextFieldKeyA = new JFormattedTextField(mask);
		formattedTextFieldKeyA.setHorizontalAlignment(SwingConstants.CENTER);
		formattedTextFieldKeyA.setBounds(10, 81, 135, 25);
		panelChange.add(formattedTextFieldKeyA);

		formattedTextFieldKeyB = new JFormattedTextField(mask);
		formattedTextFieldKeyB.setHorizontalAlignment(SwingConstants.CENTER);
		formattedTextFieldKeyB.setBounds(283, 81, 135, 25);
		panelChange.add(formattedTextFieldKeyB);

		mask1 = null;
		try {
			mask1 = new MaskFormatter(HEX_FORMATTER_FOUR_BYTES);
		} catch (ParseException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		mask1.setPlaceholderCharacter('_');
		formattedTextFieldAccessBits = new JFormattedTextField(mask1);
		formattedTextFieldAccessBits.setHorizontalAlignment(SwingConstants.CENTER);
		formattedTextFieldAccessBits.setBounds(160, 81, 106, 25);
		panelChange.add(formattedTextFieldAccessBits);

		JLabel lblKeyA = new JLabel("Key A");
		lblKeyA.setHorizontalAlignment(SwingConstants.CENTER);
		lblKeyA.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblKeyA.setBounds(10, 112, 135, 16);
		panelChange.add(lblKeyA);

		JLabel lblKeyB = new JLabel("Key B");
		lblKeyB.setHorizontalAlignment(SwingConstants.CENTER);
		lblKeyB.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblKeyB.setBounds(283, 112, 135, 16);
		panelChange.add(lblKeyB);

		JLabel lblAccessBits = new JLabel("Access Bits");
		lblAccessBits.setHorizontalAlignment(SwingConstants.CENTER);
		lblAccessBits.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblAccessBits.setBounds(160, 114, 106, 16);
		panelChange.add(lblAccessBits);

		btnRead = new JButton("Read");
		btnRead.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnRead.setEnabled(true);
		btnRead.setBounds(36, 139, 168, 25);
		panelChange.add(btnRead);

		btnUpdate = new JButton("Update");
		btnUpdate.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnUpdate.setEnabled(true);
		btnUpdate.setBounds(224, 139, 168, 25);
		panelChange.add(btnUpdate);

		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(0);

					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 {
					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 == "Mifare Standard 1K") {
						maximumSector = 0x0F;
						displayMessage("\r\nCard Type: Mifare Standard 1K");
					} else if (cardName == "Mifare Standard 4K") {
						maximumSector = 0x27;
						displayMessage("\r\nCard Type: Mifare Standard 4K");
					} else {
						displayErrorMessage("Card not supported. Please present Mifare Ultralight card.\r\n");
						return;
					}

					lbl39.setText("00 - " + String.format(("%02X"), maximumSector) + " hex");

					btnInitialize.setEnabled(false);
					btnConnect.setEnabled(false);
					buttonReset.setEnabled(true);
					comboBoxReader.setEnabled(false);

					setEnabled(panel, 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());
				}
			}
		});

		btnLoadKey.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] key = new byte[6];
				byte keyNumber = 0x20;

				if (textFieldKeyStore.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				keyNumber = Helper.getBytes(textFieldKeyStore.getText())[0];

				if (keyNumber > 0x01) {
					displayErrorMessage("Please key-in Key Store Number from 00 to 01.");
					return;
				}

				if (formattedTextFieldValueInput.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				key = Helper.getBytes(formattedTextFieldValueInput.getText());

				if (key.length < 6) {
					displayErrorMessage("Invalid input length. Length must be 6 bytes.");
					return;
				}

				try {
					displayMessage("Load Authentication Key");
					if (readerFunctions.loadAuthKey(keyNumber, key))
						displayMessage("Load Key success");
					else
						displayMessage("Load Key fail");

					setEnabled(panelFunction, true);

				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnAuthenticate.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte sectorNumber = 0x00;
				ReaderFunctions.KEYTYPES keyType = ReaderFunctions.KEYTYPES.KEYTYPE_A;

				byte keyNumber = 0x20;

				if (textFieldSector.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				sectorNumber = Helper.getBytes(textFieldSector.getText())[0];

				if (sectorNumber > maximumSector) {
					if (cardName == "Mifare Standard 1K")
						displayErrorMessage("Invalid Sector Number. Valid Sector Number for Mifare 1K: 0 - 0F (hex).");
					else if (cardName == "Mifare Standard 4K")
						displayErrorMessage("Invalid Sector Number. Valid Sector Number for Mifare 4K: 0 - 27 (hex).");
					return;
				}

				if (textFieldKeyStoreNumber.getText().length() == 0) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				keyNumber = Helper.getBytes(textFieldKeyStoreNumber.getText())[0];

				if (keyNumber > 0x01) {
					displayErrorMessage("Please key-in Key Store Number from 00 to 01.");
					return;
				}

				if (rdbtnKeyB.isSelected())
					keyType = ReaderFunctions.KEYTYPES.KEYTYPE_B;

				if (cardName == "Mifare Standard 4K") {
					currentSector = sectorNumber;

					// Mifare 4K is oragnizsed in 32 sectors with 4 blocks
					// and in 8 sectors with 16 blocks
					if (currentSector <= 31) {
						currentSectorTrailer = (byte) ((currentSector * 4) + 3);
					} else {
						// 127 is the physical address of the last block(sector trailer) of the 32nd
						// sector
						currentSectorTrailer = 127;

						// Succeeding sector contains 16 blocks
						currentSectorTrailer += (byte) (((currentSector - 32) * 16) + 16);
					}
				} else {
					currentSector = sectorNumber;

					currentSectorTrailer = (byte) ((currentSector * 4) + 3);
				}

				try {

					displayMessage("\nAuthenticate Key");
					if (readerFunctions.authenticate(currentSectorTrailer, keyType, keyNumber)) {
						displayMessage("Successful Authentication" + "\r\n");
						setEnabled(panelChange, true);
					} else
						displayMessage("Authenticate Failed!");

				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnRead.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

				byte[] sectorTrailer;

				try {

					displayMessage("\nRead Binary");
					sectorTrailer = mifareClassic.readBinaryBlock(currentSectorTrailer);
					displayMessage("Read Success");

					// Mifare does not allow you to read the actual key A.
					// We will leave this field as blank.
					formattedTextFieldKeyA.setText("");
					formattedTextFieldAccessBits.setText(Helper.byteArrayToString(sectorTrailer, 6, 4, true));
					formattedTextFieldKeyB.setText(Helper.byteArrayToString(sectorTrailer, 10, 6, true));

					JOptionPane.showMessageDialog(null,
							"NOTE: Mifare does not allow user to read the actual key A. 'Key A' field will be set to empty.",
							"Information", JOptionPane.INFORMATION_MESSAGE);

				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnUpdate.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] keyA, accessBits, keyB;
				byte[] newSectorTrailer = new byte[16];

				if (formattedTextFieldKeyA.getText().length() == 0 || formattedTextFieldKeyA.getText().length() != 6) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				if (formattedTextFieldAccessBits.getText().length() == 0
						|| formattedTextFieldAccessBits.getText().length() != 4) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				if (formattedTextFieldKeyB.getText().length() == 0 || formattedTextFieldKeyB.getText().length() != 6) {
					displayErrorMessage("Please key-in numeric value for Page Number.");
					return;
				}

				keyA = Helper.getBytes(formattedTextFieldKeyA.getText());
				accessBits = Helper.getBytes(formattedTextFieldAccessBits.getText());
				keyB = Helper.getBytes(formattedTextFieldKeyB.getText());

				if (!Helper.byteArrayIsEqual(accessBits,
						new byte[] { (byte) 0xFF, (byte) 0x07, (byte) 0x80, (byte) 0x69 }, 4)) {
					if (JOptionPane.showConfirmDialog(null, "IMPORTANT: Check Access Bits!\r\n\r\n"
							+ "Please make sure that the access bits are valid you may refer to Mifare Classic manual for more information.\r\n"
							+ "If the access bits are invalid the sector will PERMANENTLY lock it self.\r\n\r\nContinue?") != 0) {
						return;
					}

				}

				JOptionPane.showMessageDialog(null,
						"Please take note of the following: \r\n\r\n" + "Sector: " + Byte.toString(currentSector)
								+ "\r\nKey A : " + Helper.byteAsString(keyA, true) + "\r\nKey B : "
								+ Helper.byteAsString(keyB, true) + "\r\nAccess Bits : "
								+ Helper.byteAsString(accessBits, true));

				try {

					// Append Key A
					System.arraycopy(keyA, 0, newSectorTrailer, 0, 6);

					// Append Access Bits
					System.arraycopy(accessBits, 0, newSectorTrailer, 6, 4);

					// Append key B
					System.arraycopy(keyB, 0, newSectorTrailer, 10, 6);

					displayMessage("\nUpdate Binary");
					mifareClassic.updateBinaryBlock(currentSectorTrailer, newSectorTrailer, (byte) 0x10);

					displayMessage("Sector     : " + Byte.toString(currentSector));
					displayMessage("New Key A  : " + Helper.byteAsString(keyA, true));
					displayMessage("New Key B  : " + Helper.byteAsString(keyB, true));
					displayMessage("Access Bits: " + Helper.byteAsString(accessBits, true));
					displayMessage("Update Success!\r\n");

				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});
	}

	void resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);

		setEnabled(panel, false);
		setEnabled(panelFunction, false);
		setEnabled(panelChange, false);

		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));
	}
}
