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.JTextField;

import AcsCommon.Helper;
import Pcsc.CardSelector;
import Pcsc.PcscReader;
import Pcsc.ReaderEvent;
import Pcsc.ReaderEvent.TransmitApduEventArg;

import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;

import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.awt.event.ActionEvent;

public class ISO15693ReadWrite extends JFrame implements ReaderEvent.TransmitApduHandler {

	/**
	 * 
	 */
	enum Command
	{
		NONE,
		GET_INFORMATION,
		READ_SINGLE_BLOCK,
		WRITE_SINGLE_BLOCK,
		READ_MULTIPLE_BLOCKS,
		WRITE_MULTIPLE_BLOCKS,
		LOCK_BLOCK,
		GET_MULTIPLE_BLOCKS_SECURITY_STATUS
	}
	private static final long serialVersionUID = 1L;
	private JFrame frame;
	private JTextField textField;
	private JComboBox<String> comboBoxReader;
	private JButton btnInitialize;
	private JButton btnConnect;
	private JButton buttonClear;
	private JButton buttonReset;
	private JButton buttonQuit;
	private JTextArea textArea;
	private JButton btnGetData;
	private JButton btnGetInformation;
	private JButton btnReadSingleBlock;
	private JButton btnWriteSingleBlock;
	private JButton btnReadMultipleBlocks;
	private JButton btnWriteMultipleBlocks;
	private JButton btnGetSecurityStatus;
	private JButton btnLockBlock;
	private JSpinner spnBlockNo;
	private JSpinner spnNumBlocks;

	private PcscReader readerControl = new PcscReader();
	private CardSelector cardSelector = new CardSelector(readerControl);
	private ISO15693 iso15693= new ISO15693(readerControl);
	private Command command = Command.NONE;
	private int blockSize = 4;
	private int maxNumBlocks = 16;
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ISO15693ReadWrite window = new ISO15693ReadWrite();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ISO15693ReadWrite() {
		// 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, 771, 412);
		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, 346, 20);
		frame.getContentPane().add(comboBoxReader);

		btnInitialize = new JButton("Initialize");
		btnInitialize.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnInitialize.setEnabled(true);
		btnInitialize.setBounds(10, 66, 160, 25);
		frame.getContentPane().add(btnInitialize);

		btnConnect = new JButton("Connect");
		btnConnect.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnConnect.setEnabled(false);
		btnConnect.setBounds(196, 66, 160, 25);
		frame.getContentPane().add(btnConnect);

		JLabel lblApduLogs = new JLabel("APDU Logs:");
		lblApduLogs.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblApduLogs.setBounds(366, 11, 87, 16);
		frame.getContentPane().add(lblApduLogs);

		buttonClear = new JButton("Clear");
		buttonClear.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonClear.setBounds(366, 340, 125, 25);
		frame.getContentPane().add(buttonClear);

		buttonReset = new JButton("Reset");
		buttonReset.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonReset.setEnabled(false);
		buttonReset.setBounds(498, 340, 110, 25);
		frame.getContentPane().add(buttonReset);

		buttonQuit = new JButton("Quit");
		buttonQuit.setFont(new Font("Verdana", Font.PLAIN, 12));
		buttonQuit.setBounds(614, 340, 125, 25);
		frame.getContentPane().add(buttonQuit);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(366, 33, 373, 296);
		frame.getContentPane().add(scrollPane);

		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setText("");
		textArea.setLineWrap(true);

		btnGetData = new JButton("Get Data");
		btnGetData.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnGetData.setEnabled(true);
		btnGetData.setBounds(10, 102, 346, 25);
		frame.getContentPane().add(btnGetData);

		btnGetInformation = new JButton("Get System Info");
		btnGetInformation.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnGetInformation.setEnabled(true);
		btnGetInformation.setBounds(10, 138, 346, 25);
		frame.getContentPane().add(btnGetInformation);

		btnGetSecurityStatus = new JButton("Get Security Status");
		btnGetSecurityStatus.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnGetSecurityStatus.setEnabled(true);
		btnGetSecurityStatus.setBounds(10, 174, 150, 25);
		frame.getContentPane().add(btnGetSecurityStatus);

		btnLockBlock = new JButton("Lock Block");
		btnLockBlock.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnLockBlock.setEnabled(true);
		btnLockBlock.setBounds(206, 174, 150, 25);
		frame.getContentPane().add(btnLockBlock);

		btnReadSingleBlock = new JButton("Read Single Block");
		btnReadSingleBlock.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnReadSingleBlock.setEnabled(true);
		btnReadSingleBlock.setBounds(10, 210, 150, 25);
		frame.getContentPane().add(btnReadSingleBlock);

		btnWriteSingleBlock = new JButton("Write Single Block");
		btnWriteSingleBlock.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnWriteSingleBlock.setEnabled(true);
		btnWriteSingleBlock.setBounds(10, 246, 150, 25);
		frame.getContentPane().add(btnWriteSingleBlock);

		btnReadMultipleBlocks = new JButton("Read Multiple Blocks");
		btnReadMultipleBlocks.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnReadMultipleBlocks.setEnabled(true);
		btnReadMultipleBlocks.setBounds(206, 210, 150, 25);
		frame.getContentPane().add(btnReadMultipleBlocks);

		btnWriteMultipleBlocks = new JButton("Write Multiple Blocks");
		btnWriteMultipleBlocks.setFont(new Font("Verdana", Font.PLAIN, 12));
		btnWriteMultipleBlocks.setEnabled(true);
		btnWriteMultipleBlocks.setBounds(206, 246, 150, 25);
		frame.getContentPane().add(btnWriteMultipleBlocks);

		JLabel lblData = new JLabel("Data");
		lblData.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblData.setBounds(10, 318, 87, 16);
		frame.getContentPane().add(lblData);

		textField = new JTextField();
		textField.setBounds(11, 345, 345, 20);
		textField.setDocument(new JTextFieldLimit(blockSize * 2, true));
		frame.getContentPane().add(textField);
		textField.setColumns(10);

		JLabel lblBlockNo = new JLabel("Block No.");
		lblBlockNo.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblBlockNo.setBounds(10, 284, 87, 16);
		frame.getContentPane().add(lblBlockNo);

		SpinnerModel smBlockNo = new SpinnerNumberModel(0, 0, maxNumBlocks - 1, 1);
		spnBlockNo = new JSpinner(smBlockNo);
		spnBlockNo.setBounds(75, 282, 85, 20);
		frame.getContentPane().add(spnBlockNo);

		JLabel lblNumBlocks = new JLabel("No. of Blocks");
		lblNumBlocks.setFont(new Font("Verdana", Font.PLAIN, 12));
		lblNumBlocks.setBounds(185, 284, 87, 16);
		frame.getContentPane().add(lblNumBlocks);

		SpinnerModel smNumBlocks = new SpinnerNumberModel(1, 1, maxNumBlocks, 1);
		spnNumBlocks = new JSpinner(smNumBlocks);
		spnNumBlocks.setBounds(271, 282, 85, 20);
		frame.getContentPane().add(spnNumBlocks);

		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]);

					for (int j = 0; j < readerList.length; j++) {
						if (readerList[j].contains("PICC")) {							
							comboBoxReader.setSelectedIndex(j);
							break;
						}
					}

					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 (readerControl.getAtr().length > 12 && readerControl.getAtr()[12] == 0x0B) {
						displayMessage("\r\nChip Type: ISO15693");
						displayMessage("Card Name: " + cardName);
					} else {
						displayErrorMessage("Card not supported. Please use ACOS3 card.\r\n");
						return;
					}

					btnInitialize.setEnabled(false);
					btnConnect.setEnabled(false);
					buttonReset.setEnabled(true);
					comboBoxReader.setEnabled(false);
					btnGetData.setEnabled(true);
					btnGetInformation.setEnabled(true);
					btnReadSingleBlock.setEnabled(true);
					btnWriteSingleBlock.setEnabled(true);
					btnReadMultipleBlocks.setEnabled(true);
					btnWriteMultipleBlocks.setEnabled(true);
					btnGetSecurityStatus.setEnabled(true);
					btnLockBlock.setEnabled(true);
					spnBlockNo.setEnabled(true);
					textField.setEnabled(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) {
				try {
					displayMessage("\r\nProprietary Get Data (DSFIR+UID)");
					iso15693.getData();
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnGetInformation.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					displayMessage("\r\nGet System information by PCSC Transparent Exchange command");
					command = Command.GET_INFORMATION;
					iso15693.getSystemInformation();
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnReadSingleBlock.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int blockNo = (int) spnBlockNo.getValue();
					displayMessage("\r\nRead Single Block");
					command = Command.READ_SINGLE_BLOCK;
					iso15693.readSingleBlock((byte) blockNo);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnWriteSingleBlock.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int blockNo = (int) spnBlockNo.getValue();
					String text = textField.getText();
					if (text.length() != blockSize * 2) {
						displayErrorMessage(String.format("Please key-in the data to write %d bytes of hex value for input data", blockSize));
						textField.requestFocus();
						return;
					}
					displayMessage("\r\nWrite Single Block");
					byte[] data = Helper.getBytes(text);
					command = Command.WRITE_SINGLE_BLOCK;
					iso15693.writeSingleBlock((byte)blockNo, data);

					textField.setText("");
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnReadMultipleBlocks.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int firstBlockNo = (int) spnBlockNo.getValue();
					int numBlocks = (int) spnNumBlocks.getValue();
					displayMessage("\r\nRead Single Block");
					command = Command.READ_MULTIPLE_BLOCKS;
					iso15693.readMultipleBlocks((byte) firstBlockNo, (byte) numBlocks);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnWriteMultipleBlocks.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int firstBlockNo = (int) spnBlockNo.getValue();
					int numBlocks = (int) spnNumBlocks.getValue();
					String text = textField.getText();
					int length = blockSize * numBlocks;
					if (text.length() != length * 2) {
						displayErrorMessage(String.format("Please key-in the data to write %d bytes of hex value for input data", length));
						textField.requestFocus();
						return;						
					}
					displayMessage("\r\nWrite Multiple Blocks");
					byte[] data = Helper.getBytes(text);
					command = Command.WRITE_MULTIPLE_BLOCKS;
					iso15693.writeMultipleBlocks((byte)firstBlockNo, (byte)numBlocks, data);

					textField.setText("");
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnGetSecurityStatus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int firstBlockNo = (int) spnBlockNo.getValue();
					int numBlocks = (int) spnNumBlocks.getValue();
					displayMessage("\r\nGet Multiple Blocks Security Status");
					command = Command.GET_MULTIPLE_BLOCKS_SECURITY_STATUS;
					iso15693.getMultipleBlocksSecurityStatus((byte) firstBlockNo, (byte) numBlocks);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		btnLockBlock.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					int blockNo = (int) spnBlockNo.getValue();
					displayMessage("\r\nLock Block");
					command = Command.LOCK_BLOCK;
					iso15693.lockBlock((byte) blockNo);
				} catch (CardException cardException) {
					displayErrorMessage(Pcsc.PcscProvider.getScardErrorMessage(cardException));
				} catch (Exception exception) {
					displayErrorMessage(exception.getMessage());
				}
			}
		});

		spnBlockNo.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				SpinnerModel smBlockNo = spnBlockNo.getModel();
				int maxValue = maxNumBlocks - (int)smBlockNo.getValue();
				SpinnerModel smNumBlocks = spnNumBlocks.getModel();
				int n = (int)smNumBlocks.getValue();
				if (n > maxValue)
					n = maxValue;
				smNumBlocks = new SpinnerNumberModel(n, 1, maxNumBlocks, 1);
				setSpinnerModel(spnNumBlocks, smNumBlocks);
			}    
		});

		spnNumBlocks.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				textField.setDocument(new JTextFieldLimit((int)spnNumBlocks.getValue() * blockSize * 2, true));
			}    
		});
	}

	void resetControls() {
		textArea.setText("");
		btnInitialize.setEnabled(true);
		btnConnect.setEnabled(false);
		buttonReset.setEnabled(false);
		comboBoxReader.removeAllItems();
		comboBoxReader.setEnabled(false);
		btnGetData.setEnabled(false);
		btnGetInformation.setEnabled(false);
		btnReadSingleBlock.setEnabled(false);
		btnWriteSingleBlock.setEnabled(false);
		btnReadMultipleBlocks.setEnabled(false);
		btnWriteMultipleBlocks.setEnabled(false);
		btnGetSecurityStatus.setEnabled(false);
		btnLockBlock.setEnabled(false);
		spnBlockNo.setEnabled(false);

		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);
	}

	byte[] hexToBytes(String hex) {
		if (hex == null || hex.length() % 2 != 0) {
			throw new IllegalArgumentException("Invalid hexadecimal string: " + hex);
		}

		byte[] bytes = new byte[hex.length() / 2];
		for (int i = 0; i < bytes.length; i++) {
			int index = i * 2;
			int hex1 = Character.digit(hex.charAt(index), 16);
			int hex2 = Character.digit(hex.charAt(index + 1), 16);

			if (hex1 == -1 || hex2 == -1) {
				throw new IllegalArgumentException("Invalid hexadecimal string: " + hex);
			}

			bytes[i] = (byte) ((hex1 << 4) + hex2);
		}

		return bytes;
	}

	String bytesToHex(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (byte b : bytes) {
			sb.append(String.format("%02X", b));
		}
		return sb.toString();
	}

	byte[] subBytes(byte[] bytes, int offset, int count) {
		if (offset < 0 || offset >= bytes.length) {
			throw new IndexOutOfBoundsException("Invalid offset: " + offset);
		}

		if (count < 0 || offset + count > bytes.length) {
			throw new IndexOutOfBoundsException("Invalid count: " + count);
		}

		byte[] result = new byte[count];
		System.arraycopy(bytes, offset, result, 0, count);
		return result;
	}

	public void addPropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(listener);
	}

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(listener);
	}

	void setSpinnerModel(JSpinner spinner, SpinnerModel newModel) {
		SpinnerModel oldModel = spinner.getModel();
		spinner.setModel(newModel);
		propertyChangeSupport.firePropertyChange("spinnerModel", oldModel, newModel);
	}

	@Override
	public void onSendCommand(TransmitApduEventArg event) {
		String text = event.getAsString(true);
		displayMessage("<< " + text);
	}

	private void decodeGetInformation(byte[] data) {
		if (data.length < 11 || data[data.length - 2] != (byte)0x90) {
			displayMessage("Get Information failed");
			return;
		}
		int count = 0;
		byte flag = data[0];
		String text = String.format("Info Flags: %02X\r\nUID: %s", flag, bytesToHex(subBytes(data, 1, 8)));
		count += 9;
		if ((flag & 0x01) != 0)
			text += String.format("\r\nDSFID: %02X",  data[count++]);
		if ((flag & 0x02) != 0)
			text += String.format("\r\nAFI: %02X",  data[count++]);
		if ((flag & 0x04) != 0) {
			maxNumBlocks = data[count++] + 1;
			blockSize = data[count++] + 1;
			SpinnerModel smBlockNo = new SpinnerNumberModel(0, 0, maxNumBlocks - 1, 1);
			setSpinnerModel(spnBlockNo, smBlockNo);
			SpinnerModel smNumBlocks = new SpinnerNumberModel(1, 1, maxNumBlocks, 1);
			setSpinnerModel(spnNumBlocks, smNumBlocks);
			text += String.format("\r\nBlock Size: %d\r\nNumber of Blocks: %d", blockSize, maxNumBlocks);
		}
		if ((flag & 0x08) != 0)
			text += String.format("\r\nIC Reference: %02X",  data[count++]);
		text += "\r";
		displayMessage(text);
	}

	private void decodeReadSingleBlock(byte[] data) {
		if (data.length >= 3 && data[data.length - 2] == (byte)0x90)
			displayMessage(String.format("Data: %s\r\n", bytesToHex(subBytes(data, 0, data.length - 2))));
	}

	private void decodeWriteSingleBlock(byte[] data) {
		if (data.length >= 2 && data[data.length - 2] == (byte)0x90)
			displayMessage("Write Single Block Sucessful\r\n");
	}

	private void decodeReadMultipleBlocks(byte[] data) {
		if (data.length >= 3 && data[data.length - 2] == (byte)0x90)
			displayMessage(String.format("Data: %s\r\n", bytesToHex(subBytes(data, 0, data.length - 2))));
	}

	private void decodeWriteMultipleBlocks(byte[] data) {
		if (data.length >= 2 && data[data.length - 2] == (byte)0x90)
			displayMessage("Write Multiple Blocks Sucessful\r\n");
	}

	private void decodeGetMultipleBlocksSecurityStatus(byte[] data) {
		if (data.length >= 3 && data[data.length - 2] == (byte)0x90)
			displayMessage(String.format("Security Status: %s\r\n", bytesToHex(subBytes(data, 0, data.length - 2))));		
	}

	private void decodeLockBlock(byte[] data) {
		if (data.length >= 2 && data[data.length - 2] == (byte)0x90)
			displayMessage("Lock Block Successful\r\n");
	}

	@Override
	public void onReceiveCommand(TransmitApduEventArg event) {
		String text = event.getAsString(true);
		byte[] data = hexToBytes(text.replace(" ", ""));
		displayMessage(">> " + text);
		switch(command) {
		case GET_INFORMATION:
			decodeGetInformation(data);
			break;
		case READ_SINGLE_BLOCK:
			decodeReadSingleBlock(data);
			break;
		case WRITE_SINGLE_BLOCK:
			decodeWriteSingleBlock(data);
			break;
		case READ_MULTIPLE_BLOCKS:
			decodeReadMultipleBlocks(data);
			break;
		case WRITE_MULTIPLE_BLOCKS:
			decodeWriteMultipleBlocks(data);
			break;
		case GET_MULTIPLE_BLOCKS_SECURITY_STATUS:
			decodeGetMultipleBlocksSecurityStatus(data);
			break;
		case LOCK_BLOCK:
			decodeLockBlock(data);
			break;
		default:
			break;
		}
	}

}
