package UltimaDemo;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;

import net.miginfocom.swing.MigLayout;
import SDK.SDKlib;
import SDK.SDKlib.Action;
import SDK.SDKlib.BitsPerChar;
import SDK.SDKlib.BitsPerInch;
import SDK.SDKlib.Coercivity;
import SDK.SDKlib.EncodingType;
import SDK.SDKlib.MagData2;
import SDK.SDKlib.MagDef;
import SDK.SDKlib.Return;
import SDK.SDKlib.Verify;

import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;

import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;

import static UltimaDemo.Utils.*;
import static UltimaDemo.Main.tabbedPane;

//====================================================================================

class Encoding {
	private final String CLEAR       = "CLEAR";
	private final String MAGSTART    = "MAGSTART";
	private final String ENCODEMAG   = "ENCODEMAG";
	private final String ENCODETYPE  = "ENCODETYPE";
	private final String READMAG     = "READMAG";
	private final String READTRACKS  = "READTRACKS";
	private final String TRACKCLICK1 = "TRACKCLICK1";
	private final String TRACKCLICK2 = "TRACKCLICK2";
	private final String TRACKCLICK3 = "TRACKCLICK3";

	private final ActionHandler ah = new ActionHandler();

	public static final JPanel panelEncoding = new JPanel();
	
	private final JPanel panelMagStart = new JPanel();
	private final JButton btnMagstart = new JButton("Mag Start");
	private final JRadioButton rdbtnGet = new JRadioButton("Get");
	private final JRadioButton rdbtnSet = new JRadioButton("Set");
	private final JLabel lblStartPosition = new JLabel("Start Position");
	private final JSpinner spinnerMagStart = new JSpinner();

	private final JLabel labelType = new JLabel("Type");
	private final JComboBox<ComboItem> comboEncoding = new JComboBox<ComboItem>();
	private final JLabel labelCoercivity = new JLabel("Coercivity");
	private final JComboBox<ComboItem> comboCoercivity = new JComboBox<ComboItem>();
	private final JCheckBox checkVerify = new JCheckBox("Verify");
	private final JLabel labelTrack1 = new JLabel("Track 1");
	private final JCheckBox checkTrack1Write = new JCheckBox("");
	private final JTextField textTrack1 = new JTextField("THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG");
	private final JLabel labelTrack2 = new JLabel("Track 2");
	private final JCheckBox checkTrack2Write = new JCheckBox("");
	private final JTextField textTrack2 = new JTextField();
	private final JLabel labelTrack3 = new JLabel("Track 3");
	private final JCheckBox checkTrack3Write = new JCheckBox("");
	private final JTextField textTrack3 = new JTextField();
	private final JLabel labelBitsPerChar = new JLabel("Bits Per Char");
	private final JLabel labelBitsPerInch = new JLabel("Bits Per Inch");
	private final JLabel labelConfigTrack1 = new JLabel("Track 1");
	private final JComboBox<ComboItem> comboT1BPC = new JComboBox<ComboItem>();
	private final JComboBox<ComboItem> comboT1BPI = new JComboBox<ComboItem>();
	private final JLabel labelConfigTrack2 = new JLabel("Track 2");
	private final JComboBox<ComboItem> comboT2BPC = new JComboBox<ComboItem>();
	private final JComboBox<ComboItem> comboT2BPI = new JComboBox<ComboItem>();
	private final JLabel labelConfigTrack3 = new JLabel("Track 3");
	private final JComboBox<ComboItem> comboT3BPC = new JComboBox<ComboItem>();
	private final JComboBox<ComboItem> comboT3BPI = new JComboBox<ComboItem>();
	private final JButton btnEncodeMag = new JButton("Encode Mag");

	private final JCheckBox checkTrack1Read = new JCheckBox("Track 1");
	private final JCheckBox checkTrack2Read = new JCheckBox("Track 2");
	private final JCheckBox checkTrack3Read = new JCheckBox("Track 3");
	private final JButton btnReadMagTracks = new JButton("Read Mag Tracks");
	private final JButton btnReadMag = new JButton("Read Mag");

	private final JTextArea textEncodingMsg = new JTextArea();
	private final JScrollPane scrollPane = new JScrollPane(textEncodingMsg);    
	private final JButton btnClear = new JButton("Clear");

	private HANDLE hSession;
	private SDKlib SDK;

	// ====================================================================================

	public Encoding() {
		panelEncoding.setLayout(new MigLayout("", "[grow]", "[][][grow]"));

	// ===== Encode Panel =====
		JPanel panelSelections = new JPanel();
		panelSelections.setBorder(new TitledBorder(null, "", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelEncoding.add(panelSelections, "cell 0 0,grow");
		panelSelections.setLayout(new MigLayout("", "[42.00][][20%:n:20%,grow][20%:n:20%,grow][20%:n:20%,grow][grow]", "[][][][][][][][]"));
		labelType.setHorizontalAlignment(SwingConstants.LEFT);

		panelSelections.add(labelType, "cell 0 0 2 1,alignx left");
		panelSelections.add(comboEncoding, "cell 2 0,growx");
		comboEncoding.addItem(new ComboItem("ISO",    EncodingType.ISO));
		comboEncoding.addItem(new ComboItem("JIS2",   EncodingType.JIS2));
		comboEncoding.addItem(new ComboItem("Custom", EncodingType.RAW));
		comboEncoding.setActionCommand(ENCODETYPE);
		comboEncoding.addActionListener(ah);

		panelSelections.add(labelCoercivity, "flowx,cell 3 0,alignx trailing");
		panelSelections.add(comboCoercivity, "cell 4 0,growx");
		comboCoercivity.addItem(new ComboItem("Default", Coercivity.DEFAULT));
		comboCoercivity.addItem(new ComboItem("HiCo", Coercivity.HI_CO));
		comboCoercivity.addItem(new ComboItem("LoCo", Coercivity.LO_CO));

		panelSelections.add(checkVerify, "cell 5 0,alignx right");
		checkVerify.setSelected(true);

		panelSelections.add(labelTrack1, "cell 0 1");
		panelSelections.add(checkTrack1Write, "cell 1 1");
		checkTrack1Write.setSelected(true);
		checkTrack1Write.setActionCommand(TRACKCLICK1);
		checkTrack1Write.addActionListener(ah);
		panelSelections.add(textTrack1, "cell 2 1 4 1,growx");

		labelTrack2.setEnabled(false);
		panelSelections.add(labelTrack2, "cell 0 2");
		panelSelections.add(checkTrack2Write, "cell 1 2");
		checkTrack2Write.setActionCommand(TRACKCLICK2);
		checkTrack2Write.addActionListener(ah);
		textTrack2.setEnabled(false);
		panelSelections.add(textTrack2, "cell 2 2 4 1,growx");

		labelTrack3.setEnabled(false);
		panelSelections.add(labelTrack3, "cell 0 3");
		panelSelections.add(checkTrack3Write, "cell 1 3");
		checkTrack3Write.setActionCommand(TRACKCLICK3);
		checkTrack3Write.addActionListener(ah);
		textTrack3.setEnabled(false);
		panelSelections.add(textTrack3, "cell 2 3 4 1,growx");

		panelSelections.add(labelBitsPerChar, "cell 0 5,alignx center");
		panelSelections.add(labelBitsPerInch, "cell 0 6,alignx center");

		panelSelections.add(labelConfigTrack1, "cell 2 4,alignx center");

		panelSelections.add(comboT1BPC, "cell 2 5,growx");
		comboT1BPC.addItem(new ComboItem("Default", BitsPerChar.DEFAULT));
		comboT1BPC.addItem(new ComboItem("1", BitsPerChar.BPC1));
		comboT1BPC.addItem(new ComboItem("5", BitsPerChar.BPC5));
		comboT1BPC.addItem(new ComboItem("7", BitsPerChar.BPC7));
		
		panelSelections.add(comboT1BPI, "cell 2 6,growx");
		comboT1BPI.addItem(new ComboItem("Default", BitsPerInch.DEFAULT));
		comboT1BPI.addItem(new ComboItem("210", BitsPerInch.BPI210));
		comboT1BPI.addItem(new ComboItem("75", BitsPerInch.BPI75));


		labelConfigTrack2.setEnabled(false);
		panelSelections.add(labelConfigTrack2, "cell 3 4,alignx center");

		comboT2BPC.setEnabled(false);
		panelSelections.add(comboT2BPC, "cell 3 5,growx");
		comboT2BPC.addItem(new ComboItem("Default", BitsPerChar.DEFAULT));
		comboT2BPC.addItem(new ComboItem("1", BitsPerChar.BPC1));
		comboT2BPC.addItem(new ComboItem("5", BitsPerChar.BPC5));
		comboT2BPC.addItem(new ComboItem("7", BitsPerChar.BPC7));

		comboT2BPI.setEnabled(false);
		panelSelections.add(comboT2BPI, "cell 3 6,growx");
		comboT2BPI.addItem(new ComboItem("Default", BitsPerInch.DEFAULT));
		comboT2BPI.addItem(new ComboItem("210", BitsPerInch.BPI210));
		comboT2BPI.addItem(new ComboItem("75", BitsPerInch.BPI75));
		

		labelConfigTrack3.setEnabled(false);
		panelSelections.add(labelConfigTrack3, "cell 4 4,alignx center");
		
		comboT3BPC.setEnabled(false);
		panelSelections.add(comboT3BPC, "cell 4 5,growx");
		comboT3BPC.addItem(new ComboItem("Default", BitsPerChar.DEFAULT));
		comboT3BPC.addItem(new ComboItem("1", BitsPerChar.BPC1));
		comboT3BPC.addItem(new ComboItem("5", BitsPerChar.BPC5));
		comboT3BPC.addItem(new ComboItem("7", BitsPerChar.BPC7));
		
		comboT3BPI.setEnabled(false);
		panelSelections.add(comboT3BPI, "cell 4 6,growx");
		comboT3BPI.addItem(new ComboItem("Default", BitsPerInch.DEFAULT));
		comboT3BPI.addItem(new ComboItem("210", BitsPerInch.BPI210));
		comboT3BPI.addItem(new ComboItem("75", BitsPerInch.BPI75));

		panelSelections.add(btnEncodeMag, "cell 0 7 6 1,alignx center");
		btnEncodeMag.setActionCommand(ENCODEMAG);
		btnEncodeMag.addActionListener(ah);
		
		// ===== Mag Start =====
		panelEncoding.add(panelMagStart, "cell 0 1,grow");
		panelMagStart.setBorder(new TitledBorder(null, "", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panelMagStart.setLayout(new MigLayout("", "[][74.00][58.00][125.00][grow]", "[]"));
		rdbtnGet.setSelected(true);
		panelMagStart.add(rdbtnGet, "flowy,cell 0 0");
		panelMagStart.add(rdbtnSet, "cell 0 0");
		
		panelMagStart.add(lblStartPosition, "cell 1 0");
		spinnerMagStart.setModel(new SpinnerNumberModel(8000, 0, 85000, 1));
		panelMagStart.add(spinnerMagStart, "cell 2 0,growx");
		
		panelMagStart.add(btnMagstart, "cell 4 0,growx");
		btnMagstart.setActionCommand(MAGSTART);
		btnMagstart.addActionListener(ah);

		// ===== Read Mag Panel =====
		JPanel panelResults = new JPanel();
		panelResults.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), 
				                                "", 
				                                TitledBorder.LEADING,
				                                TitledBorder.TOP, 
				                                null, 
				                                new Color(0, 0, 0)));
		panelEncoding.add(panelResults, "cell 0 2,grow");
		panelResults.setLayout(new MigLayout("", "[70.00,grow][124.00][125.00][grow]", "[][][][grow][]"));

		panelResults.add(checkTrack1Read, "flowx,cell 0 0,alignx left,aligny baseline");
		panelResults.add(checkTrack2Read, "cell 0 1");
		panelResults.add(checkTrack3Read, "cell 0 2");

		panelResults.add(btnReadMagTracks, "cell 1 1,growx");
		btnReadMagTracks.setActionCommand(READTRACKS);
		btnReadMagTracks.addActionListener(ah);

		panelResults.add(btnReadMag, "cell 3 1,growx");
		btnReadMag.setActionCommand(READMAG);
		btnReadMag.addActionListener(ah);
		
		panelResults.add(scrollPane, "cell 0 3 4 1,grow");
		textEncodingMsg.setEditable(false);
		textEncodingMsg.setBorder(BorderFactory.createLineBorder(Color.black));

		panelResults.add(btnClear, "cell 0 4 4 1,alignx center");
		btnClear.setActionCommand(CLEAR);
		btnClear.addActionListener(ah);
	}

	// ====================================================================================

	void Init(HANDLE handle, SDKlib sdk) {
		tabbedPane.addTab("Mag Encoding", null, panelEncoding, null);
		this.hSession = handle;
		this.SDK = sdk;
	}

	// ====================================================================================

	private void EncodingChanged() {
		// Enable/Disable appropriate controls according to ISO or JIS2 being selected
		Boolean visible = (((ComboItem)comboEncoding.getSelectedItem()).getValue() == EncodingType.ISO);

		checkTrack1Write.setVisible(visible);
		labelConfigTrack1.setVisible(visible);
		labelTrack2.setVisible(visible);
		textTrack2.setVisible(visible);
		checkTrack2Write.setVisible(visible);
		labelConfigTrack2.setVisible(visible);
		labelTrack3.setVisible(visible);
		textTrack3.setVisible(visible);
		checkTrack3Write.setVisible(visible);
		labelConfigTrack3.setVisible(visible);

		comboT1BPC.setVisible(visible);
		comboT1BPI.setVisible(visible);
		comboT2BPC.setVisible(visible);
		comboT2BPI.setVisible(visible);
		comboT3BPC.setVisible(visible);
		comboT3BPI.setVisible(visible);
		labelBitsPerChar.setVisible(visible);
		labelBitsPerInch.setVisible(visible);
		labelTrack1.setText(visible ? "Track 1" : "Data");
	}

	// ------------------------------------------------------------------------------------

	private void CheckDataAvailable() {
		btnEncodeMag.setEnabled(   checkTrack1Write.isSelected()
				                || checkTrack2Write.isSelected() 
				                || checkTrack3Write.isSelected());
	}

	// ------------------------------------------------------------------------------------

	private void Track1Clicked() {
		// Enable/Disable the appropriate controls according to the selection
		Boolean control = checkTrack1Write.isSelected();
		labelTrack1.setEnabled(control);
		labelConfigTrack1.setEnabled(control);
		textTrack1.setEnabled(control);
		comboT1BPC.setEnabled(control);
		comboT1BPI.setEnabled(control);

		CheckDataAvailable();
	}

	// ------------------------------------------------------------------------------------

	private void Track2Clicked() {
		// Enable/Disable the appropriate controls according to the selection
		Boolean control = checkTrack2Write.isSelected();
		labelTrack2.setEnabled(control);
		labelConfigTrack2.setEnabled(control);
		textTrack2.setEnabled(control);
		comboT2BPC.setEnabled(control);
		comboT2BPI.setEnabled(control);

		CheckDataAvailable();
	}

	// ------------------------------------------------------------------------------------

	private void Track3Clicked() {
		// Enable/Disable the appropriate controls according to the selection
		Boolean control = checkTrack3Write.isSelected();
		labelTrack3.setEnabled(control);
		labelConfigTrack3.setEnabled(control);
		textTrack3.setEnabled(control);
		comboT3BPC.setEnabled(control);
		comboT3BPI.setEnabled(control);

		CheckDataAvailable();
	}

	// ------------------------------------------------------------------------------------

	private void ClearMsg() {
		textEncodingMsg.setText(null);
	}

	// ====================================================================================

	private final char ISO_7_BIT_START_SENTINEL = '%';
	private final char ISO_5_BIT_START_SENTINEL = ';';
	private final char ISO_END_SENTINEL         = '?';
	private final char JIS2_SENTINEL            = 0x7f;

	private void EncodeMagData() {
		CursorWait();

		// Initialise common encoding parameters
		MagDef Def = new MagDef();
		Def.encoding   = ((ComboItem)comboEncoding.getSelectedItem()).getValue();
		Def.verify     = checkVerify.isSelected() ? Verify.ON : Verify.OFF;
		Def.coercivity = ((ComboItem)comboCoercivity.getSelectedItem()).getValue();

		if (((ComboItem)comboEncoding.getSelectedItem()).getValue() == EncodingType.JIS2) {
			Def.track[0].data = JIS2_SENTINEL + textTrack1.getText() + JIS2_SENTINEL;
		}

		else {
			// ISO Track 1
			if (checkTrack1Write.isSelected()) {
				// Initialise for ISO Track 1 Encoding
				if (((ComboItem)comboEncoding.getSelectedItem()).getValue() == EncodingType.ISO) {
					Def.track[0].data = ISO_7_BIT_START_SENTINEL + textTrack1.getText() + ISO_END_SENTINEL;
				}
				else {
					Def.track[0].data = textTrack1.getText();
				}
				Def.track[0].bitsPerChar = ((ComboItem)comboT1BPC.getSelectedItem()).getValue();
				Def.track[0].bitsPerInch = ((ComboItem)comboT1BPI.getSelectedItem()).getValue();
			}

			// ISO Track 2
			if (checkTrack2Write.isSelected()) {
				// Initialise for ISO Track 2 Encoding
				if (((ComboItem)comboEncoding.getSelectedItem()).getValue() == EncodingType.ISO) {
					Def.track[1].data = ISO_5_BIT_START_SENTINEL + textTrack2.getText() + ISO_END_SENTINEL;
				}
				else {
					Def.track[1].data = textTrack2.getText();
				}
				Def.track[1].bitsPerChar = ((ComboItem)comboT2BPC.getSelectedItem()).getValue();
				Def.track[1].bitsPerInch = ((ComboItem)comboT2BPI.getSelectedItem()).getValue();
			}

			// ISO Track 3
			if (checkTrack3Write.isSelected()) {
				// Initialise for ISO Track 3 Encoding
				if (((ComboItem)comboEncoding.getSelectedItem()).getValue() == EncodingType.ISO) {
					Def.track[2].data = ISO_5_BIT_START_SENTINEL + textTrack3.getText() + ISO_END_SENTINEL;
				}
				else {
					Def.track[2].data = textTrack3.getText();
				}
				Def.track[2].bitsPerChar = ((ComboItem)comboT3BPC.getSelectedItem()).getValue();
				Def.track[2].bitsPerInch = ((ComboItem)comboT3BPI.getSelectedItem()).getValue();
			}
		}

		// Call SDK to perform the mag encoding
		byte sdkReturn = SDK.ID_EncodeMag(hSession, Def);
		if (sdkReturn != Return.SUCCESS) {
			DisplayError("EncodeMagStripe", sdkReturn);
		} 
		else {
			// Wait for the printer to complete the encoding
			sdkReturn = WaitForPrinterToFinish(hSession, SDK);
			if (sdkReturn != Return.SUCCESS) {
				DisplayError("EncodeMagStripeWait", sdkReturn);
			}
		}

		CursorDefault();
	}

	// ------------------------------------------------------------------------------------

	private void ReadMagData() {
		CursorWait();

		MagData2 MD2 = new MagData2();
		byte sdkReturn = SDK.ID_ReadMag(hSession, 
										MD2,
										((ComboItem)comboEncoding.getSelectedItem()).getValue());
		if (sdkReturn != Return.SUCCESS) {
			DisplayError("ReadMagStripe", sdkReturn);
		}
		else {
			textEncodingMsg.append("(1): " + MD2.track1 + "\n");
			textEncodingMsg.append("(2): " + MD2.track2 + "\n");
			textEncodingMsg.append("(3): " + MD2.track3 + "\n\n");
		}

		CursorDefault();
	}

	// ------------------------------------------------------------------------------------

	private void ReadMagTracks() {
		CursorWait();

		MagData2 MD = new MagData2();
		byte Tracks = 0;
		if (checkTrack1Read.isSelected()) Tracks |= 0x01;
		if (checkTrack2Read.isSelected()) Tracks |= 0x02;
		if (checkTrack3Read.isSelected()) Tracks |= 0x04;

		if (Tracks != 0) {
			byte sdkReturn = SDK.ID_ReadMagTracks(hSession, 
											      MD,
											      ((ComboItem)comboEncoding.getSelectedItem()).getValue(),
											      Tracks);
			if (sdkReturn != Return.SUCCESS) {
				DisplayError("ReadMagStripe", sdkReturn);
			}
	
			else {
				if (checkTrack1Read.isSelected()) textEncodingMsg.append("(1): " + MD.track1 + "\n");
				if (checkTrack2Read.isSelected()) textEncodingMsg.append("(2): " + MD.track2 + "\n");
				if (checkTrack3Read.isSelected()) textEncodingMsg.append("(3): " + MD.track3 + "\n");
				textEncodingMsg.append("\n");
			}
		}

		CursorDefault();
	}

	// ====================================================================================

	private byte GetMagStart(IntByReference magStart) {
		byte sdkReturn = SDK.ID_MagStart(hSession, Action.READ, magStart);
		if (sdkReturn != Return.SUCCESS) {
			DisplayError("GetMagStart", sdkReturn);
		}
		else {
			MagStartToControl(magStart);
		}

		return sdkReturn;
	}

	// ------------------------------------------------------------------------------------

	private void MagStartToControl(IntByReference magStart) {
		spinnerMagStart.setValue(magStart.getValue());
	}

	// ------------------------------------------------------------------------------------

	private void MagStartToMsgBox(IntByReference magStart, byte action) {
		textEncodingMsg.append("MagStart" + Separator(action) + magStart.getValue() + "\n\n");
	}

	// ------------------------------------------------------------------------------------

	private void MagStart() {
		IntByReference magStart = new IntByReference((int)spinnerMagStart.getValue());
		
		if (rdbtnGet.isSelected()) {
			if (GetMagStart(magStart) == Return.SUCCESS) {
				MagStartToMsgBox(magStart, Action.READ);
			}
		}
		else {
			//Call the SDK to write to the driver
			byte sdkReturn = SDK.ID_MagStart(hSession, Action.WRITE, magStart);
			if (sdkReturn != Return.SUCCESS) {
				DisplayError("SetMagStart", sdkReturn);
			}
			else {
				MagStartToMsgBox(magStart, Action.WRITE);
			}
		}
	}

	// ====================================================================================

	class ActionHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			switch (e.getActionCommand()) {
			case CLEAR:       ClearMsg();        break;
			case MAGSTART:    MagStart();        break;
			case ENCODETYPE:  EncodingChanged(); break;
			case ENCODEMAG:   EncodeMagData();   break;
			case TRACKCLICK1: Track1Clicked();   break;
			case TRACKCLICK2: Track2Clicked();   break;
			case TRACKCLICK3: Track3Clicked();   break;
			case READTRACKS:  ReadMagTracks();   break;
			case READMAG:     ReadMagData();     break;
			}
		}
	}
}
