import java.awt.Component;
import java.awt.Container;
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.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;

import AcsCommon.*;
import Pcsc.*;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.JCheckBox;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.ActionEvent;

public class Other_PICC extends JFrame implements ReaderEvent.TransmitApduHandler, KeyListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textCLA;
	private JTextField textINS;
	private JTextField textP1;
	private JTextField textP2;
	private JTextField textLc;
	private JTextField textLe;
	private JButton buttonQuit;
	private JButton buttonReset;
	private JButton buttonClear;
	private JComboBox<String> comboBoxReader;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JTextArea textArea;
	private JLabel lblApduLogs;
	private JCheckBox chckbxISOCard;
	private JButton btnGetData;
	private JButton btnSendCardCommand;
	private JTextArea textAreaInput;

	private PcscReader readerControl = new PcscReader();
	private PICCClass piccClass = new PICCClass(readerControl);
	private JPanel panelGetData;
	private JPanel panelSendCard;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Other_PICC window = new Other_PICC();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public Other_PICC() {
		// 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, 756, 502);
		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, 252, 20);
		frame.getContentPane().add(comboBoxReader);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(10, 37, 353, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(10, 68, 353, 25);
		frame.getContentPane().add(btnConnect);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(369, 37, 361, 379);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		textArea.setLineWrap(true);
		scrollPane.setViewportView(textArea);

		lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(369, 15, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		panelGetData = new JPanel();
		panelGetData.setBorder(
				new TitledBorder(null, "Get Data Function", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelGetData.setBounds(10, 104, 349, 62);
		frame.getContentPane().add(panelGetData);
		panelGetData.setLayout(null);

		chckbxISOCard = new JCheckBox("ISO 14443A Card");
		chckbxISOCard.setBounds(23, 24, 151, 23);
		panelGetData.add(chckbxISOCard);

		btnGetData = new JButton("Get Data");
		btnGetData.setBounds(235, 24, 89, 23);
		panelGetData.add(btnGetData);

		panelSendCard = new JPanel();
		panelSendCard.setBorder(
				new TitledBorder(null, "Send Card Command", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelSendCard.setBounds(10, 177, 349, 275);
		frame.getContentPane().add(panelSendCard);
		panelSendCard.setLayout(null);

		btnSendCardCommand = new JButton("Send Card Command");
		btnSendCardCommand.setBounds(160, 241, 179, 23);
		panelSendCard.add(btnSendCardCommand);

		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(10, 99, 329, 131);
		panelSendCard.add(scrollPane_1);

		textAreaInput = new JTextArea();
		scrollPane_1.setViewportView(textAreaInput);
		textAreaInput.setText("");
		textAreaInput.setEnabled(false);

		JLabel lblDataIn = new JLabel("Data In");
		lblDataIn.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblDataIn.setBounds(10, 72, 87, 16);
		panelSendCard.add(lblDataIn);

		JLabel lblCLA = new JLabel("CLA");
		lblCLA.setHorizontalAlignment(SwingConstants.CENTER);
		lblCLA.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblCLA.setBounds(10, 21, 40, 16);
		panelSendCard.add(lblCLA);

		JLabel lblINS = new JLabel("INS");
		lblINS.setHorizontalAlignment(SwingConstants.CENTER);
		lblINS.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblINS.setBounds(60, 21, 40, 16);
		panelSendCard.add(lblINS);

		JLabel lblP1 = new JLabel("P1");
		lblP1.setHorizontalAlignment(SwingConstants.CENTER);
		lblP1.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblP1.setBounds(110, 21, 40, 16);
		panelSendCard.add(lblP1);

		JLabel lblP2 = new JLabel("P2");
		lblP2.setHorizontalAlignment(SwingConstants.CENTER);
		lblP2.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblP2.setBounds(160, 21, 40, 16);
		panelSendCard.add(lblP2);

		JLabel lblLc = new JLabel("Lc");
		lblLc.setHorizontalAlignment(SwingConstants.CENTER);
		lblLc.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblLc.setBounds(210, 21, 40, 16);
		panelSendCard.add(lblLc);

		JLabel lblLe = new JLabel("Le");
		lblLe.setHorizontalAlignment(SwingConstants.CENTER);
		lblLe.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblLe.setBounds(260, 21, 40, 16);
		panelSendCard.add(lblLe);

		textCLA = new JTextField();
		textCLA.setHorizontalAlignment(SwingConstants.CENTER);
		textCLA.setColumns(2);
		textCLA.setBounds(10, 41, 40, 20);
		textCLA.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textCLA);

		textINS = new JTextField();
		textINS.setHorizontalAlignment(SwingConstants.CENTER);
		textINS.setColumns(2);
		textINS.setBounds(60, 41, 40, 20);
		textINS.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textINS);

		textP1 = new JTextField();
		textP1.setHorizontalAlignment(SwingConstants.CENTER);
		textP1.setColumns(2);
		textP1.setBounds(110, 41, 40, 20);
		textP1.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textP1);

		textP2 = new JTextField();
		textP2.setHorizontalAlignment(SwingConstants.CENTER);
		textP2.setColumns(2);
		textP2.setBounds(160, 41, 40, 20);
		textP2.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textP2);

		textLc = new JTextField();
		textLc.setHorizontalAlignment(SwingConstants.CENTER);
		textLc.setColumns(2);
		textLc.setBounds(210, 41, 40, 20);
		textLc.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textLc);

		textLe = new JTextField();
		textLe.setHorizontalAlignment(SwingConstants.CENTER);
		textLe.setColumns(2);
		textLe.setBounds(260, 41, 40, 20);
		textLe.setDocument(new JTextFieldLimit(2));
		panelSendCard.add(textLe);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(369, 427, 106, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(485, 427, 110, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(605, 427, 125, 25);
		frame.getContentPane().add(buttonQuit);

		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);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage() + "\r\n");
				}

				btnConnect.setEnabled(true);
			}
		});

		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()));

					btnInitialize.setEnabled(false);
					btnConnect.setEnabled(false);
					buttonReset.setEnabled(true);
					comboBoxReader.setEnabled(false);
					setEnabled(panelGetData, true);
					setEnabled(panelSendCard, 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());
				}
			}
		});

		btnGetData.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

				displayMessage("Get Data");
				byte uIso14443A;
				byte[] uCardVersion;

				if (chckbxISOCard.isSelected())
					uIso14443A = 0x01;
				else
					uIso14443A = 0x00;

				try {
					uCardVersion = piccClass.getData(uIso14443A);

					if (chckbxISOCard.isSelected())
						displayMessage("\r\nUID: " + Helper.byteAsString(uCardVersion, true));
					else
						displayMessage("\r\nATS: " + Helper.byteAsString(uCardVersion, true));

				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnSendCardCommand.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				byte uCla, uIns, uP1, uP2;
				byte uLc = 0x00;
				byte uLe = 0x00;
				byte[] uData = null;
				int iCaseType = 0;

				try {
					// Validate inputs
					if (textCLA.getText().length() < 2) {
						displayErrorMessage("Please key-in hex value for CLA.");
						textCLA.requestFocus();
						return;
					}

					if (textINS.getText().length() < 2) {
						displayErrorMessage("Please key-in hex value for INS.");
						textINS.requestFocus();
						return;
					}

					if (textP1.getText().length() < 2) {
						displayErrorMessage("Please key-in hex value for P1.");
						textP1.requestFocus();
						return;
					}

					if (textP2.getText().length() < 2) {
						displayErrorMessage("Please key-in hex value for P2.");
						textP2.requestFocus();
						return;
					}

					uCla = (byte) ((Integer) Integer.parseInt(textCLA.getText(), 16)).byteValue();
					uIns = (byte) ((Integer) Integer.parseInt(textINS.getText(), 16)).byteValue();
					uP1 = (byte) ((Integer) Integer.parseInt(textP1.getText(), 16)).byteValue();
					uP2 = (byte) ((Integer) Integer.parseInt(textP2.getText(), 16)).byteValue();

					if (Integer.parseInt(textLc.getText()) > 0 && textLc.getText() != "00") {
						if (textAreaInput.getText().length() == 0) {
							displayErrorMessage("Please key-in hex value for Data In.");
							textAreaInput.requestFocus();
							return;
						}
					}

					if (Integer.parseInt(textLc.getText()) > 0) {
						uLc = (byte) ((Integer) Integer.parseInt(textLc.getText(), 16)).byteValue();

						if (uLc > 0x00) {
							uData = Helper.getBytes(textAreaInput.getText(), " ");
							if (uData == null) {
								displayErrorMessage("\r\nData In field has invalid format");
								textAreaInput.requestFocus();
								return;
							}

							if ((int) uLc != uData.length) {
								displayErrorMessage("Length of Data does not match Lc");
								textAreaInput.requestFocus();
								return;
							}
						}
					}

					if (textLe.getText().length() > 0) {
						uLe = (byte) ((Integer) Integer.parseInt(textLe.getText(), 16)).byteValue();
					}

					if (Integer.parseInt(textLc.getText()) == 0) {
						if (uLe == 0x00)
							iCaseType = 0;
						else
							iCaseType = 1;
					} else {
						if (uLe == 0x00)
							iCaseType = 2;
						else
							iCaseType = 3;
					}

					displayMessage("Send Command");
					piccClass.sendCommand(iCaseType, uCla, uIns, uP1, uP2, uLc, uLe, uData);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});
	}

	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 resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);

		setEnabled(panelGetData, false);
		setEnabled(panelSendCard, false);

		displayMessage("Program Ready");
	}

	@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));
	}

	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 keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}
}
