package interfaces;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

import analyzer.Constants;
import analyzer.StructureWriter;
import analyzer.TrainFileReader;
import analyzer.TrainFileWriter;
import analyzer.Trainer;

public class TrainGUInterface extends JFrame {

	private JPanel contentPane;
	private JTextField textFieldRecord;
	private JNumberTextField textFieldHiddenNeurons;
	private JNumberTextField textFieldLearningRate;
	private JNumberTextField textFieldMaximumIterations;
	private JLabel lblStatusReady;
	private JLabel lblProgress;
	private JButton btnStart;
	private JButton btnStop;
	private JButton btnSaveStructure;
	private JButton btnAddRecord;
	private JCheckBox checkBox;
	private TrainGUInterface frame;

	private String defaultChooserDirectory;

	private Trainer trainer;

	private ProgressWatcher progressWatcher;

	/**
	 * Launch the application.
	 */
	// public static void main(String[] args) {
	// EventQueue.invokeLater(new Runnable() {
	// public void run() {
	// try {
	// TrainGUInterface frame = new TrainGUInterface();
	// frame.setVisible(true);
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	// }
	// });
	// }

	/**
	 * Create the frame.
	 */
	public TrainGUInterface() {
		defaultChooserDirectory = System.getProperty("user.dir");
		frame = this;
		setResizable(false);
		setTitle("Neural Network Training Phase");
		// setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		addWindowListener(new java.awt.event.WindowAdapter() {
			@Override
			public void windowClosing(java.awt.event.WindowEvent windowEvent) {
				if (JOptionPane.showConfirmDialog(frame, "Close this window?", "Confirm ?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
					new MainGUInterface().setVisible(true);
					frame.dispose();
				}
			}
		});
		setBounds(100, 100, 533, 359);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);

		JLabel lblAddNewRecord = new JLabel("Add new record to database:");
		lblAddNewRecord.setBounds(10, 23, 173, 14);
		contentPane.add(lblAddNewRecord);

		textFieldRecord = new JTextField();
		textFieldRecord.setUI(new HintTextFieldUI(" Ex: 20.04.2015 2,6,21,22,15,48", true));
		textFieldRecord.setBounds(193, 24, 201, 20);
		contentPane.add(textFieldRecord);
		textFieldRecord.setColumns(10);

		btnAddRecord = new JButton("Add");
		btnAddRecord.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				String record = textFieldRecord.getText().trim();
				String[] content = record.split(" ");
				if (content.length != 2) {
					JOptionPane.showMessageDialog(frame, "Invalid record format.", "Format error", JOptionPane.INFORMATION_MESSAGE);
					return;
				}

				String[] input = content[0].split("\\.");
				if (input.length != 3) {
					JOptionPane.showMessageDialog(frame, "Invalid record format.", "Format error", JOptionPane.INFORMATION_MESSAGE);
					return;
				}

				String[] output = content[1].split(",");
				if (output.length != 6) {
					JOptionPane.showMessageDialog(frame, "Invalid record format.", "Format error", JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				TrainFileWriter trainFileWriter = new TrainFileWriter(Constants.TRAINING_FILE);
				if (trainFileWriter.writeRecord(record))
					JOptionPane.showMessageDialog(frame, "Record has been added to database.", "Add Success", JOptionPane.INFORMATION_MESSAGE);
				else
					JOptionPane.showMessageDialog(frame, "Write Error.", "Operation Failed", JOptionPane.ERROR_MESSAGE);

			}
		});
		btnAddRecord.setBounds(404, 23, 89, 23);
		contentPane.add(btnAddRecord);

		JLabel lblNumberOfHidden = new JLabel("Number of Hidden Neurons:");
		lblNumberOfHidden.setBounds(10, 63, 173, 14);
		contentPane.add(lblNumberOfHidden);

		textFieldHiddenNeurons = new JNumberTextField();
		// textFieldHiddenNeurons.setUI(new HintTextFieldUI(" Range 3~7", true));
		textFieldHiddenNeurons.setBounds(193, 63, 201, 20);
		contentPane.add(textFieldHiddenNeurons);
		textFieldHiddenNeurons.setColumns(10);

		JLabel lblLearningRate = new JLabel("Learning Rate:");
		lblLearningRate.setBounds(10, 103, 158, 14);
		contentPane.add(lblLearningRate);

		JLabel lblMaximumIterations = new JLabel("Maximum Iterations:");
		lblMaximumIterations.setBounds(10, 143, 158, 14);
		contentPane.add(lblMaximumIterations);

		btnStop = new JButton("Stop");
		btnStop.setEnabled(false);
		btnStop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (trainer != null)
					trainer.stop();
			}
		});
		btnStop.setBounds(320, 238, 89, 23);
		contentPane.add(btnStop);

		btnSaveStructure = new JButton("Save Structure");
		btnSaveStructure.setEnabled(false);

		btnSaveStructure.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (trainer == null) {
					JOptionPane.showMessageDialog(frame, "Run the trainer to generate structure.", "No structure found", JOptionPane.INFORMATION_MESSAGE);
					return;
				}

				JFileChooser fileChooser = new JFileChooser(defaultChooserDirectory);
				if (fileChooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
					File file = fileChooser.getSelectedFile();

					try {
						defaultChooserDirectory = file.getCanonicalPath();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					StructureWriter structureWriter = new StructureWriter(trainer.getBestWeightH(), trainer.getBestWeightO(), file.getPath());
					structureWriter.write();
				}

			}
		});
		btnSaveStructure.setBounds(352, 289, 141, 23);
		contentPane.add(btnSaveStructure);

		textFieldLearningRate = new JNumberTextField();
		textFieldLearningRate.setFormat(3);
		textFieldLearningRate.setPrecision(5);
		textFieldLearningRate.setUI(new HintTextFieldUI(" Range (0.4 ~ 0.7)", true));
		textFieldLearningRate.setBounds(193, 103, 201, 20);
		contentPane.add(textFieldLearningRate);
		textFieldLearningRate.setColumns(10);

		textFieldMaximumIterations = new JNumberTextField();
		// textFieldMaximumIterations.setUI(new HintTextFieldUI(" Range (20 ~ 1000)", true));
		textFieldMaximumIterations.setBounds(193, 143, 201, 20);
		contentPane.add(textFieldMaximumIterations);
		textFieldMaximumIterations.setColumns(10);

		btnStart = new JButton("Start");
		btnStart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (btnStart.getText().equalsIgnoreCase("Start")) {
					float learningRate = 0;
					int maxIterations = 0, hiddenNeurons = 0;
					try {
						hiddenNeurons = textFieldHiddenNeurons.getInt();
						if (hiddenNeurons == 0) {
							JOptionPane.showMessageDialog(frame, "Enter correct hidden neurons.", "Invalid Parameter", JOptionPane.INFORMATION_MESSAGE);
							return;
						}

						learningRate = textFieldLearningRate.getFloat();
						if (learningRate == 0.0) {
							JOptionPane.showMessageDialog(frame, "Enter correct learning rate.", "Invalid Parameter", JOptionPane.INFORMATION_MESSAGE);
							return;
						}

						maxIterations = textFieldMaximumIterations.getInt();
						if (maxIterations == 0) {
							JOptionPane.showMessageDialog(frame, "Enter correct maximum iterations.", "Invalid Parameter", JOptionPane.INFORMATION_MESSAGE);
							return;
						}
					} catch (Exception e) {
						JOptionPane.showMessageDialog(frame, "Please enter correct parameters as specified", "Invalid Parameter", JOptionPane.INFORMATION_MESSAGE);
						return;
					}

					TrainerThread trainerThread = new TrainerThread(learningRate, hiddenNeurons, maxIterations);
					trainerThread.start();
				} else if (btnStart.getText().equalsIgnoreCase("Pause")) {
					if (trainer != null)
						trainer.pause();

				} else if (btnStart.getText().equalsIgnoreCase("Resume")) {
					if (trainer != null) {
						TrainerThread trainerThread = new TrainerThread();
						trainerThread.start();
					}
				}

			}
		});
		btnStart.setBounds(115, 238, 89, 23);
		contentPane.add(btnStart);

		lblStatusReady = new JLabel("Status: Ready.");
		lblStatusReady.setBounds(10, 298, 210, 14);
		contentPane.add(lblStatusReady);

		lblProgress = new JLabel("Progress: ");
		lblProgress.setBounds(10, 281, 210, 14);
		contentPane.add(lblProgress);

		progressWatcher = new ProgressWatcher(lblProgress);

		checkBox = new JCheckBox("");
		checkBox.setBounds(10, 183, 400, 23);
		checkBox.setText("Monitor Progress (Slower Performance)");
		checkBox.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {
				if (trainer == null)
					return;

				if (checkBox.isSelected())
					trainer.setCallback(progressWatcher);
				else {
					trainer.removeCallback();
					lblProgress.setText("Progress: ");
				}
			}
		});
		contentPane.add(checkBox);
	}

	public static class ProgressWatcher {
		JLabel mProgressLabel;

		public ProgressWatcher(JLabel progressLabel) {
			mProgressLabel = progressLabel;
		}

		public void callback(int maxIterations, int currentIteration) {
			mProgressLabel.setText("Progress: " + currentIteration);
		}
	}

	public class TrainerThread extends Thread {
		public TrainerThread(float learningRate, int hiddenNeurons, int maxIterations) {
			TrainFileReader trainFileReader = new TrainFileReader(Constants.TRAINING_FILE);

			trainer = new Trainer(trainFileReader.getNormalizedInput(), trainFileReader.getNormalizedOutput(), hiddenNeurons, learningRate, maxIterations, progressWatcher);
		}

		public TrainerThread() {

		}

		@Override
		public void run() {
			super.run();
			lblStatusReady.setText("Status: Training In Progress.");
			btnStart.setText("Pause");
			btnStop.setEnabled(true);
			btnSaveStructure.setEnabled(false);
			btnAddRecord.setEnabled(false);
			// checkBox.setEnabled(false);
			textFieldHiddenNeurons.setEnabled(false);
			textFieldLearningRate.setEnabled(false);
			textFieldMaximumIterations.setEnabled(false);
			if (checkBox.isSelected())
				trainer.setCallback(progressWatcher);
			else
				trainer.removeCallback();

			trainer.startTraining();
			if (trainer.getStatus() == Trainer.STATUS_STOP) {
				btnStart.setText("Start");
				btnStop.setEnabled(false);
				btnAddRecord.setEnabled(true);
				// checkBox.setEnabled(true);
				lblStatusReady.setText("Status: Training Stopped.");
				textFieldHiddenNeurons.setEnabled(true);
				textFieldLearningRate.setEnabled(true);
				textFieldMaximumIterations.setEnabled(true);
			} else if (trainer.getStatus() == Trainer.STATUS_PAUSE) {
				btnStart.setText("Resume");
				lblStatusReady.setText("Status: Training Paused.");
			}
			btnSaveStructure.setEnabled(true);
		}
	}
}
