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 Acos.Acos3;
import AcsCommon.Helper;
import Pcsc.CardSelector;
import Pcsc.PcscReader;
import Pcsc.ReaderEvent;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.border.EtchedBorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;

import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.DefaultComboBoxModel;

public class ACOS3ConfigureATR extends JFrame implements ReaderEvent.TransmitApduHandler {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textField;
	private JTextField textField_1;
	private JTextField textField_2;
	private JTextField textField_3;
	private JTextField textField_4;
	private JTextField textField_5;
	private JTextField textField_6;
	private JTextField textField_7;
	private JTextField textField_8;
	private JTextField textField_9;
	private JTextField textField_10;
	private JTextField textField_11;
	private JTextField textField_12;
	private JTextField textField_13;
	private JTextField textField_14;
	private JTextField textFieldTA;
	private JTextField textFieldＴ0;

	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 JComboBox<String> comboBoxReader;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JButton buttonClear;
	private JButton buttonReset;
	private JButton buttonQuit;
	private JTextArea textArea;
	private JButton btnUpdateATR;
	private JComboBox<String> comboBoxReaderBaudRate;
	private JComboBox<String> comboBoxReaderBytes;
	private JPanel panelHistoricalBytes;
	private JPanel panel;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ACOS3ConfigureATR window = new ACOS3ConfigureATR();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ACOS3ConfigureATR() {
		// 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, 780, 549);
		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, 373, 20);
		frame.getContentPane().add(comboBoxReader);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(215, 66, 168, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(215, 102, 168, 25);
		frame.getContentPane().add(btnConnect);

		JLabel lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(393, 11, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(393, 474, 106, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(509, 474, 110, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(629, 474, 125, 25);
		frame.getContentPane().add(buttonQuit);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(393, 35, 361, 428);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setText("");
		textArea.setLineWrap(true);

		panelHistoricalBytes = new JPanel();
		panelHistoricalBytes.setLayout(null);
		panelHistoricalBytes.setEnabled(false);
		panelHistoricalBytes.setBorder(new TitledBorder(
				new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)),
				"Historical Bytes", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
		panelHistoricalBytes.setBounds(10, 328, 373, 171);
		frame.getContentPane().add(panelHistoricalBytes);

		btnUpdateATR = new JButton("Update ATR");
		btnUpdateATR.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnUpdateATR.setEnabled(true);
		btnUpdateATR.setBounds(220, 137, 143, 25);
		panelHistoricalBytes.add(btnUpdateATR);

		textField = new JTextField();
		textField.setText("");
		textField.setHorizontalAlignment(SwingConstants.CENTER);
		textField.setColumns(10);
		textField.setDocument(new JTextFieldLimit(2));
		textField.setBounds(31, 29, 53, 25);
		panelHistoricalBytes.add(textField);

		textField_1 = new JTextField();
		textField_1.setText("");
		textField_1.setHorizontalAlignment(SwingConstants.CENTER);
		textField_1.setColumns(10);
		textField_1.setDocument(new JTextFieldLimit(2));
		textField_1.setBounds(94, 29, 53, 25);
		panelHistoricalBytes.add(textField_1);

		textField_2 = new JTextField();
		textField_2.setText("");
		textField_2.setHorizontalAlignment(SwingConstants.CENTER);
		textField_2.setColumns(10);
		textField_2.setDocument(new JTextFieldLimit(2));
		textField_2.setBounds(157, 29, 53, 25);
		panelHistoricalBytes.add(textField_2);

		textField_3 = new JTextField();
		textField_3.setText("");
		textField_3.setHorizontalAlignment(SwingConstants.CENTER);
		textField_3.setColumns(10);
		textField_3.setDocument(new JTextFieldLimit(2));
		textField_3.setBounds(220, 29, 53, 25);
		panelHistoricalBytes.add(textField_3);

		textField_4 = new JTextField();
		textField_4.setText("");
		textField_4.setHorizontalAlignment(SwingConstants.CENTER);
		textField_4.setColumns(10);
		textField_4.setDocument(new JTextFieldLimit(2));
		textField_4.setBounds(283, 29, 53, 25);
		panelHistoricalBytes.add(textField_4);

		textField_5 = new JTextField();
		textField_5.setText("");
		textField_5.setHorizontalAlignment(SwingConstants.CENTER);
		textField_5.setColumns(10);
		textField_5.setDocument(new JTextFieldLimit(2));
		textField_5.setBounds(31, 65, 53, 25);
		panelHistoricalBytes.add(textField_5);

		textField_6 = new JTextField();
		textField_6.setText("");
		textField_6.setHorizontalAlignment(SwingConstants.CENTER);
		textField_6.setColumns(10);
		textField_6.setDocument(new JTextFieldLimit(2));
		textField_6.setBounds(94, 65, 53, 25);
		panelHistoricalBytes.add(textField_6);

		textField_7 = new JTextField();
		textField_7.setText("");
		textField_7.setHorizontalAlignment(SwingConstants.CENTER);
		textField_7.setColumns(10);
		textField_7.setDocument(new JTextFieldLimit(2));
		textField_7.setBounds(157, 65, 53, 25);
		panelHistoricalBytes.add(textField_7);

		textField_8 = new JTextField();
		textField_8.setText("");
		textField_8.setHorizontalAlignment(SwingConstants.CENTER);
		textField_8.setColumns(10);
		textField_8.setDocument(new JTextFieldLimit(2));
		textField_8.setBounds(220, 65, 53, 25);
		panelHistoricalBytes.add(textField_8);

		textField_9 = new JTextField();
		textField_9.setText("");
		textField_9.setHorizontalAlignment(SwingConstants.CENTER);
		textField_9.setColumns(10);
		textField_9.setDocument(new JTextFieldLimit(2));
		textField_9.setBounds(283, 65, 53, 25);
		panelHistoricalBytes.add(textField_9);

		textField_10 = new JTextField();
		textField_10.setText("");
		textField_10.setHorizontalAlignment(SwingConstants.CENTER);
		textField_10.setColumns(10);
		textField_10.setDocument(new JTextFieldLimit(2));
		textField_10.setBounds(31, 101, 53, 25);
		panelHistoricalBytes.add(textField_10);

		textField_11 = new JTextField();
		textField_11.setText("");
		textField_11.setHorizontalAlignment(SwingConstants.CENTER);
		textField_11.setColumns(10);
		textField_11.setDocument(new JTextFieldLimit(2));
		textField_11.setBounds(94, 101, 53, 25);
		panelHistoricalBytes.add(textField_11);

		textField_12 = new JTextField();
		textField_12.setText("");
		textField_12.setHorizontalAlignment(SwingConstants.CENTER);
		textField_12.setColumns(10);
		textField_12.setDocument(new JTextFieldLimit(2));
		textField_12.setBounds(157, 101, 53, 25);
		panelHistoricalBytes.add(textField_12);

		textField_13 = new JTextField();
		textField_13.setText("");
		textField_13.setHorizontalAlignment(SwingConstants.CENTER);
		textField_13.setColumns(10);
		textField_13.setDocument(new JTextFieldLimit(2));
		textField_13.setBounds(220, 101, 53, 25);
		panelHistoricalBytes.add(textField_13);

		textField_14 = new JTextField();
		textField_14.setText("");
		textField_14.setHorizontalAlignment(SwingConstants.CENTER);
		textField_14.setColumns(10);
		textField_14.setDocument(new JTextFieldLimit(2));
		textField_14.setBounds(283, 101, 53, 25);
		panelHistoricalBytes.add(textField_14);

		panel = new JPanel();
		panel.setBorder(new TitledBorder(null, "", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panel.setBounds(10, 141, 373, 171);
		frame.getContentPane().add(panel);
		panel.setLayout(null);

		JLabel lblBaudRate = new JLabel("Card Baud Rate");
		lblBaudRate.setBounds(10, 11, 110, 14);
		panel.add(lblBaudRate);

		comboBoxReaderBaudRate = new JComboBox<String>();
		comboBoxReaderBaudRate.setModel(new DefaultComboBoxModel(new String[] { "9,600 bps", "13,950 bps", "27,900 bps",
				"55,800 bps", "111,600 bps", "115,200 bps", "192,000 bps", "223,200 bps" }));
		comboBoxReaderBaudRate.setFont(new Font("Verdana", Font.PLAIN, 12));
		comboBoxReaderBaudRate.setEnabled(false);
		comboBoxReaderBaudRate.setBounds(10, 36, 246, 20);
		panel.add(comboBoxReaderBaudRate);

		comboBoxReaderBytes = new JComboBox<String>();
		comboBoxReaderBytes.setModel(new DefaultComboBoxModel(new String[] { "00", "01", "02", "03", "04", "05", "06",
				"07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F" }));
		comboBoxReaderBytes.setFont(new Font("Verdana", Font.PLAIN, 12));
		comboBoxReaderBytes.setEnabled(false);
		comboBoxReaderBytes.setBounds(10, 115, 246, 20);
		panel.add(comboBoxReaderBytes);

		JLabel lblHistoricalBytes = new JLabel("No. of Historical Bytes");
		lblHistoricalBytes.setBounds(10, 90, 138, 14);
		panel.add(lblHistoricalBytes);

		JLabel lblT0 = new JLabel("T0");
		lblT0.setHorizontalAlignment(SwingConstants.CENTER);
		lblT0.setBounds(295, 90, 33, 14);
		panel.add(lblT0);

		JLabel lblTA = new JLabel("TA");
		lblTA.setHorizontalAlignment(SwingConstants.CENTER);
		lblTA.setBounds(295, 11, 33, 14);
		panel.add(lblTA);

		textFieldTA = new JTextField();
		textFieldTA.setEditable(false);
		textFieldTA.setText("");
		textFieldTA.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldTA.setColumns(10);
		textFieldTA.setBounds(285, 31, 53, 25);
		panel.add(textFieldTA);

		textFieldＴ0 = new JTextField();
		textFieldＴ0.setEditable(false);
		textFieldＴ0.setText("");
		textFieldＴ0.setHorizontalAlignment(SwingConstants.CENTER);
		textFieldＴ0.setColumns(10);
		textFieldＴ0.setBounds(285, 110, 53, 25);
		panel.add(textFieldＴ0);

		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");
						displayMessage("Active Protocol " + readerControl.getActiveProtocol());
						displayMessage("ATR: " + Helper.byteAsString(readerControl.getAtr(), true));

						textFieldＴ0.setText(Integer.toHexString(((Byte) readerControl.getAtr()[1]).intValue() & 0xFF)
								.toUpperCase());
						textFieldTA.setText(Integer.toHexString(((Byte) readerControl.getAtr()[2]).intValue() & 0xFF)
								.toUpperCase());

						setBaudRate();
						byte[] b = Helper.getBytes(textFieldＴ0.getText());
						comboBoxReaderBytes.setSelectedIndex(b[0] & 0x0F);

						setEnabled(panelHistoricalBytes, b[0] & 0x0F);
					} 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(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());
				}
			}
		});

		comboBoxReaderBytes.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				setEnabled(panelHistoricalBytes, false);
				setEnabled(panelHistoricalBytes, comboBoxReaderBytes.getSelectedIndex());
			}
		});

		btnUpdateATR.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte[] dataIn = new byte[comboBoxReaderBytes.getSelectedIndex()];

				Component components[] = ((Container) panelHistoricalBytes).getComponents();
				for (int i = 1; i < comboBoxReaderBytes.getSelectedIndex() + 1; i++) {
					if (components[i] != null && components[i] instanceof JTextField) {
						String x = ((JTextField) components[i]).getText();
						dataIn[i - 1] = Helper.getBytes(x)[0]; // <- Here is the recursive call.
					}
				}
				try {
					// 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) 0x07 });

					// write to third record of FF 04
					displayMessage("\nWrite Record");
					acos3.writeRecord((byte) 0x00, (byte) 0x00, dataIn);
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}
			}
		});
	}

	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 setEnabled(Component component, int enableField) {
		component.setEnabled(true);
		// 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 < enableField + 1; i++) {
				if (components[i] != null) {
					components[i].setEnabled(true);
				}
			}
		}
	}

	void resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);

		setEnabled(panel, false);
		setEnabled(panelHistoricalBytes, false);

		textField.setText("");
		textField_1.setText("");
		textField_2.setText("");
		textField_3.setText("");
		textField_4.setText("");
		textField_5.setText("");
		textField_6.setText("");
		textField_7.setText("");
		textField_8.setText("");
		textField_9.setText("");
		textField_10.setText("");
		textField_11.setText("");
		textField_12.setText("");
		textField_13.setText("");
		textField_14.setText("");

		displayMessage("Program Ready");
	}

	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 void setBaudRate() {
		switch (textFieldTA.getText()) {
		case "92":
			comboBoxReaderBaudRate.setSelectedIndex(1);
			break;
		case "93":
			comboBoxReaderBaudRate.setSelectedIndex(2);
			break;
		case "94":
			comboBoxReaderBaudRate.setSelectedIndex(3);
			break;
		case "95":
			comboBoxReaderBaudRate.setSelectedIndex(4);
			break;
		case "18":
			comboBoxReaderBaudRate.setSelectedIndex(5);
			break;
		case "19":
			comboBoxReaderBaudRate.setSelectedIndex(6);
			break;
		case "96":
			comboBoxReaderBaudRate.setSelectedIndex(7);
			break;
		default:
			textFieldTA.setText("11");
			comboBoxReaderBaudRate.setSelectedIndex(0);
			break;

		}
	}

}
