package de.tu_darmstadt.tk.SmartHomeNetworkSim.view.popups;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JSeparator;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.SimulationController;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;

import javax.swing.JCheckBox;
import javax.swing.JFileChooser;

/**
 * Window for configuring, starting and stopping the Simulation
 * 
 * @author Andreas T. Meyer-Berg
 */
@SuppressWarnings("serial")
public class SimulationConfigurator extends JFrame implements Observer{
	/**
	 * Textfield for editing the start time
	 */
	private JTextField tfStartTimeLong;
	/**
	 * Textfield for editing the end time
	 */
	private JTextField tfEndTimeLong;
	/**
	 * Textfield for editing the step duration
	 */
	private JTextField tfStepDuration;
	/**
	 * Checkbox if packets should be printed
	 */
	private JCheckBox chckbxPrintpackets;
	/**
	 * Checkbox if packets should be split into different Files for each link
	 */
	private JCheckBox chckbxSplitLinks;
	/**
	 * Label which shows the name of the export File
	 */
	private JLabel lblExportFileName;
	/**
	 * Label which shows the current Time of the simulation
	 */
	private JLabel lblCurrentTimeShow;
	/**
	 * Button for starting/stopping the simulation
	 */
	private JButton btnStartStop;
	/**
	 * Button for specifying the export file
	 */
	private JButton btnExportFile;
	/**
	 * ProgressionBar which shows the progression. Values are between 0 and
	 * 10000 where 0 represents the start time, and 10000 represents the end
	 * time.
	 */
	private JProgressBar progressBar;
	/**
	 * Controller of the Application
	 */
	private Controller controller;
	/**
	 * Simulation controller
	 */
	private SimulationController sim;
	/**
	 * Reference to this instance for inner classes
	 */
	private SimulationConfigurator that = this;
	/**
	 * Mutex to disable
	 */
	private boolean mutex = false;

	/**
	 * Create a new SimulationConfigurator Panel, which controls the given
	 * SimulationManager
	 * 
	 * @param control Controller of the program
	 */
	public SimulationConfigurator(Controller control) {
		this.controller = control;
		this.sim = controller.getSimulationController();
		sim.addObserver(this);
		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				sim.removeObserver(that);
			}
		});

		this.getContentPane().setMinimumSize(new Dimension(640, 310));
		this.setBounds(new Rectangle(0, 0, 640, 310));
		setResizable(false);
		getContentPane().setLayout(null);
		this.setTitle("Simulation Configuration");

		/*Buttons for setting Start/End Dates
		 * Currently not in use
		JButton btnChooseStartDate = new JButton("Choose Start Date");
		btnChooseStartDate.setBounds(12, 13, 161, 25);
		getContentPane().add(btnChooseStartDate);

		JButton btnStartTime = new JButton("Choose End Date");
		btnStartTime.setBounds(271, 13, 161, 25);
		getContentPane().add(btnStartTime);
		 */
		
		/*******************************************************************************
		 * Buttons and labels for simulation Configuration (start, end, duration, steps) 
		 ******************************************************************************/
		
		JLabel lblConfigureParameters = new JLabel("Change Simulation parameters:");
		lblConfigureParameters.setBounds(10, 10, 300, 20);
		getContentPane().add(lblConfigureParameters);
		
		JLabel lblStarttimeLong = new JLabel("Start Time (in ms):");
		lblStarttimeLong.setBounds(10, 40, 160, 20);
		getContentPane().add(lblStarttimeLong);

		tfStartTimeLong = new JTextField();
		tfStartTimeLong.setBounds(160, 40, 130, 20);
		getContentPane().add(tfStartTimeLong);
		tfStartTimeLong.setColumns(10);
		tfStartTimeLong.setText(sim.getStartTime()+"");
		

		JLabel lblEndtimeLong = new JLabel("End Time (in ms):");
		lblEndtimeLong.setBounds(300, 40, 160, 20);
		getContentPane().add(lblEndtimeLong);
		
		tfEndTimeLong = new JTextField();
		tfEndTimeLong.setBounds(450, 40, 130, 20);
		getContentPane().add(tfEndTimeLong);
		tfEndTimeLong.setColumns(10);
		
		JLabel lblStepDuration = new JLabel("Step Duration(in ms):");
		lblStepDuration.setBounds(10, 70, 160, 20);
		getContentPane().add(lblStepDuration);
		
		tfStepDuration = new JTextField();
		tfStepDuration.setBounds(160, 70, 130, 20);
		getContentPane().add(tfStepDuration);
		tfStepDuration.setColumns(10);
		
		JSeparator topSeparator = new JSeparator(JSeparator.HORIZONTAL);
		topSeparator.setBounds(0, 95, 660, 10);
		getContentPane().add(topSeparator);
		/*******************************************************************************
		 * Buttons and labels for simulation Configuration (start, end, duration, steps) 
		 ******************************************************************************/
		
		JLabel lblConfigureExports = new JLabel("Configure Packet export options:");
		lblConfigureExports.setBounds(10, 100, 255, 20);
		getContentPane().add(lblConfigureExports);
		
		JButton btnEditAlgorithms = new JButton("Edit Algorithms");
		btnEditAlgorithms.setBounds(250, 100, 180, 20);
		getContentPane().add(btnEditAlgorithms);
		btnEditAlgorithms.setToolTipText("Edit which user defined algorithms should run during the simulation.");
		btnEditAlgorithms.addActionListener(a->{ 
			new EditAlgorithmsPopUp(controller, that);
			});

		chckbxPrintpackets = new JCheckBox("Save packets");
		chckbxPrintpackets.setBounds(10, 130, 120, 20);
		getContentPane().add(chckbxPrintpackets);
		chckbxPrintpackets
		.setToolTipText("Save packets as human readable Strings to the configured File");
		
		chckbxSplitLinks = new JCheckBox("Split Links");
		chckbxSplitLinks.setBounds(130, 130, 100, 20);
		getContentPane().add(chckbxSplitLinks);
		chckbxSplitLinks
		.setToolTipText("Export packets into a different file for each Link.");
		
		btnExportFile = new JButton("Choose Export File");
		btnExportFile.setBounds(250, 130, 180, 20);
		getContentPane().add(btnExportFile);
		
		lblExportFileName = new JLabel();
		lblExportFileName.setBounds(440, 130, 300, 20);
		getContentPane().add(lblExportFileName);
		
		JSeparator botSeparator = new JSeparator(JSeparator.HORIZONTAL);
		botSeparator.setBounds(0, 155, 660, 5);
		getContentPane().add(botSeparator);
		/*******************************************************************************
		 * Buttons and labels for simulation Configuration (start, end, duration, steps) 
		 ******************************************************************************/
		
		JLabel lblSimulationStatus = new JLabel("Running Simulation status:");
		lblSimulationStatus.setBounds(10, 160, 300, 20);
		getContentPane().add(lblSimulationStatus);

		JLabel lblCurrentTime = new JLabel("Current Time:");
		lblCurrentTime.setBounds(10, 190, 100, 20);
		getContentPane().add(lblCurrentTime);

		lblCurrentTimeShow = new JLabel();
		lblCurrentTimeShow.setBounds(120, 190, 120, 20);
		lblCurrentTimeShow.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
		getContentPane().add(lblCurrentTimeShow);

		btnStartStop = new JButton("Start Simulation");
		btnStartStop.setBounds(10, 220, 150, 40);
		getContentPane().add(btnStartStop);
		
		
		JButton btnReset = new JButton("Reset");
		btnReset.setBounds(170, 220, 150, 40);
		btnReset.addActionListener(a -> sim.resetSimulation());
		getContentPane().add(btnReset);

		progressBar = new JProgressBar();
		progressBar.setBounds(0, 260, this.getWidth(), 30);
		getContentPane().add(progressBar);
		progressBar.setMinimum(0);
		progressBar.setMaximum(10000);
		this.update(null,null);
		initializeListener();
		}

	/**
	 * Test the GUI
	 * @param args none specified
	 */
	public static void main(String[] args) {
		SimulationConfigurator simConfig = new SimulationConfigurator(new Controller(new Model()));
		simConfig.setEnabled(true);
		simConfig.setVisible(true);
		//Prevent demon threads on close
		simConfig.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}

	@Override
	public void update(Observable o, Object arg) {
		long currentTime = sim.getCurrentTime();
		long startTime = sim.getStartTime();
		long endTime = sim.getEndTime();
		long duration = sim.getStepDuration();
		if(!mutex){
			tfStartTimeLong.setText("" + startTime);
			tfStartTimeLong.setBackground(Color.WHITE);
			tfEndTimeLong.setText("" + endTime);
			tfEndTimeLong.setBackground(Color.WHITE);
			tfStepDuration.setText("" + duration);
			tfStepDuration.setBackground(Color.WHITE);
		}
		chckbxPrintpackets.setSelected(sim.getPrintPackets());
		chckbxSplitLinks.setSelected(sim.isSplitLinkExportFiles());
		lblExportFileName.setText(sim.getExportFile().getName());
		lblExportFileName.setToolTipText("Path: "+sim.getExportFile().getPath());
		lblCurrentTimeShow.setText(currentTime+" ms");
		if(endTime!=startTime)
			progressBar.setValue((int) ((currentTime - startTime) * 10000 / (endTime - startTime)));
		else
			progressBar.setValue(10000);
		
		//Prevent manipulation of parameters during simulation
		tfStartTimeLong.setEditable(!sim.isRunning());
		tfEndTimeLong.setEditable(!sim.isRunning());
		tfStepDuration.setEditable(!sim.isRunning());
		
		//Change Button Text
		if (sim.isRunning()) {
			btnStartStop.setText("Stop Simulation");
		} else if(sim.getCurrentTime()<sim.getEndTime()){
			btnStartStop.setText("Start Simulation");
		} else{
			btnStartStop.setText("Restart Simulation");
		}
		mutex = false;
		
	}
	
	/**
	 * Add listeners to the different Textfields, Buttons etc.
	 */
	private void initializeListener(){
		/**
		 * Start Time listener
		 */
		tfStartTimeLong.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent e) {update(e);}
			@Override
			public void insertUpdate(DocumentEvent e) {update(e);}
			@Override
			public void changedUpdate(DocumentEvent e) {update(e);}
			
			private void update(DocumentEvent e){
				if(mutex)return;//Skip if document change in progress
				try{
					/**
					 * Parsed Long
					 */
					long l = Long.parseLong(tfStartTimeLong.getText());
					if(!sim.isRunning()){
						/**
						 * Set Mutex, to disable document change
						 */
						mutex = true;
						try{
							/**
							 * Catch InvalidStateException, change startTime
							 */
							sim.setStartTime(l);
						}catch(Exception e1){}
						mutex = false;
						tfStartTimeLong.setBackground(Color.WHITE);
						return;
					}
					tfStartTimeLong.setBackground(Color.WHITE);
				}catch(Exception e1){
					/**
					 * Invalid Long -> Red Background
					 */
					tfStartTimeLong.setBackground(Color.RED);					
				}
			}
		});
		/**
		 * End time listener
		 */
		tfEndTimeLong.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent e) {update(e);}
			@Override
			public void insertUpdate(DocumentEvent e) {update(e);}
			@Override
			public void changedUpdate(DocumentEvent e) {update(e);}
			
			private void update(DocumentEvent e){
				if(mutex)return;
				try{
					long l = Long.parseLong(tfEndTimeLong.getText());
					if(!sim.isRunning()){
						mutex = true;
						try{
							sim.setEndTime(l);
						}catch(Exception e1){}
						mutex = false;
						tfEndTimeLong.setBackground(Color.WHITE);
						return;
					}
					tfEndTimeLong.setBackground(Color.WHITE);
				}catch(Exception e1){
					tfEndTimeLong.setBackground(Color.RED);					
				}
			}
		});
		/**
		 * Step duration listener
		 */
		tfStepDuration.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent e) {update(e);}
			@Override
			public void insertUpdate(DocumentEvent e) {update(e);}
			@Override
			public void changedUpdate(DocumentEvent e) {update(e);}
			
			private void update(DocumentEvent e){
				if(mutex)return;
				try{
					long l = Long.parseLong(tfStepDuration.getText());
					if(l<=0)throw new NumberFormatException("Duration shall not be negative");
					if(!sim.isRunning()){
						mutex = true;
						try{
							sim.setStepDuration(l);
						}catch(Exception e1){}
						mutex = false;
						tfStepDuration.setBackground(Color.WHITE);
						return;
					}
					tfStepDuration.setBackground(Color.WHITE);
				}catch(Exception e1){
					tfStepDuration.setBackground(Color.RED);					
				}
			}
		});
		/**
		 * Start Stop listener
		 */
		btnStartStop.addActionListener(a -> {
			if(sim.isRunning()){
				sim.stopSimulation();
			}else{
				if(sim.getCurrentTime()>=sim.getEndTime()){
					//Reset
					sim.resetSimulation();
				}
				sim.startSimulation();
			}
		});
		
		chckbxPrintpackets.addActionListener(l -> sim.setPrintPackets(chckbxPrintpackets.isSelected()));
		
		chckbxSplitLinks.addActionListener(l -> sim.setSplitLinkExportFiles(chckbxSplitLinks.isSelected()));
		
		/**
		 * Export location:
		 */
		btnExportFile.addActionListener(a->{
			/**
			 * Open FileChooser, starting from the last File
			 */
			JFileChooser fc = new JFileChooser(sim.getExportFile());
			
			/**
			 * If File selected
			 */
	        if (fc.showSaveDialog(that.getParent()) == JFileChooser.APPROVE_OPTION) {
	        	/**
	        	 * and not null -> import
	        	 */
	            File file = fc.getSelectedFile();
	            if(file!=null)sim.setExportFile(file);
	       }
		});
	}
}
