package ui.view.main.old;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Hashtable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicSliderUI;

import ui.controller.Control;
import utility.ImageImport;
/**
 * This Class represents a Panel where the User can start and stop the
 * Simulation. He Can also reset the Simulation and click through every
 * Iteration.
 * 
 * @author Gruppe14
 */
public class TimePanel extends JPanel implements ActionListener{

	private static final Color PALE_RED = new Color(255, 192, 192);
	private static final int MAX_ITERATIONS=100000;
	/*
	 * variable for calculating the performance
	 */
	//private long performanceTime;
	final JButton playBtn = new JButton();
	final JButton timeResetBtn = new JButton();
	final JButton timeForwardBtn = new JButton();
	
	final JButton timeBackwardBtn = new JButton();
	JTextField iterationsField;
	final JLabel iterationsLabel=new JLabel("Iterations:" , SwingConstants.CENTER);
	JLabel hint = new JLabel("Invalid", SwingConstants.RIGHT);
	private final JPanel btnAndSpeedPanel = new JPanel();
	private final JPanel speedPanel = new JPanel();
	private final JPanel timeBtnPanel = new JPanel();
	private final JPanel iterationsPanel=new JPanel();
	private final JPanel timePanel=new JPanel();
	private ScheduledFuture<?> futureTask;
	//private LabelHint iterationsLblHint;
	JSlider timeSlider = new JSlider() {
		/**
		 *
		 */
		private static final long serialVersionUID = 1L;

		{
			// Make the slider jump to mouse position on left click
			MouseListener[] listeners = getMouseListeners();
			for (MouseListener l : listeners)
				removeMouseListener(l); // remove UI-installed TrackListener
			final BasicSliderUI ui = (BasicSliderUI) getUI();
			BasicSliderUI.TrackListener tl = ui.new TrackListener() {
				// this is where we jump to absolute value of click
				@Override
				public void mouseClicked(MouseEvent e) {
					Point p = e.getPoint();
					int value = ui.valueForXPosition(p.x);

					setValue(value);
				}

				// disable check that will invoke scrollDueToClickInTrack
				@Override
				public boolean shouldScroll(int dir) {
					return false;
				}
			};
			addMouseListener(tl);
		}
	};
	//private Model model;
	private Control controller;
	private int dragResetIteration = 0;
	private JLabel simSpeedLabel = new JLabel("Speed:");
	private JSlider speedSlider = new JSlider();
	private Timer timer;
	private boolean running = false;

	/**
	 * Constructor.
	 * 
	 * @param mod
	 *            the Model
	 * @param cont
	 *            the Controller
	 */
	public TimePanel(Control cont) {
		super();
		this.controller = cont;
		// One Iteration
		timer = new Timer(0, new ActionListener() {//TODO: Merge all these damned listeners.
			@Override
			public void actionPerformed(ActionEvent ae) {
				timeSlider.setValue(timeSlider.getValue() + 1);
				controller.setCurIteration(timeSlider.getValue());
				timer.setDelay(cont.getModel().getTimerSpeed());
				if (timeSlider.getValue() >= cont.getModel().getIterations() - 1) {
					/*/calc and print performance
					performanceTime = System.nanoTime()-performanceTime;
					
					System.out.println(performanceTime/1000000000 + " Sekunden "
							+ (performanceTime%1000000000)/1000000 + " Millisekunden "
							+ (performanceTime%1000000)/1000 + " Mikrosekunden ");
					//*/
					running = false;
					playBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/play.png", 30, 30)));
					timer.stop();
				}
			}
		});

		// Time Slider. Panels and Buttons
		this.setLayout(new BorderLayout(0, 0));
		this.setBorder(null);
		// Slider
		timeSlider.setPaintTicks(true);
		timeSlider.setPaintLabels(true);
		timeSlider.setMajorTickSpacing((int)Math.ceil(((double)cont.getModel().getIterations())/20));
		timeSlider.setMinorTickSpacing((int)Math.ceil(((double)cont.getModel().getIterations())/100));
		timeSlider.setToolTipText("Time Slider");
		timeSlider.setMaximum(cont.getModel().getIterations() - 1);
		timeSlider.setValue(0);
		timeSlider.addChangeListener(changeEvent -> controller.setCurIteration(timeSlider.getValue()));

		timeSlider.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				dragResetIteration = cont.getModel().getCurIteration();
			}
		});

		timeSlider.addMouseMotionListener(new MouseAdapter() {
			@Override
			public void mouseDragged(MouseEvent e) {
				if (dragResetIteration != cont.getModel().getCurIteration()) {
					if (running) {
						playBtn.doClick();
					}
				}
			}
		});

		// Panel
		timeBtnPanel.setBorder(null);
		timeBtnPanel.setLayout(new FlowLayout(FlowLayout.CENTER));

		// Buttons
		playBtn.setToolTipText("Play");

		playBtn.setContentAreaFilled(false);
		playBtn.setBorderPainted(false);
		playBtn.setBorder(null);
		playBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/play.png",30,30)));
		playBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(cont.getModel().getCurIteration()==cont.getModel().getIterations()-1)timeSlider.setValue(timeSlider.getMinimum());
				running = !running;
				if (running) {
					timer.setDelay(cont.getModel().getTimerSpeed());

					/*
					 * start the Performance Test
					 * /
					performanceTime = System.nanoTime();
					timeSlider.setValue(0);
					timer.setDelay(0);
					speedSlider.setValue(10);
					timeSlider.setValue(0);
					//*/
					
					timer.start();
					playBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/pause.png", 30, 30)));
				} else {
					timer.stop();
					playBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/play.png", 30, 30)));
				}
			}
		});
		timeResetBtn.setToolTipText("Reset");

		timeResetBtn.setContentAreaFilled(false);
		timeResetBtn.setBorder(null);
		timeResetBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/reset.png", 30, 30)));
		timeResetBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				timeSlider.setValue(timeSlider.getMinimum());
				controller.setCurIteration(timeSlider.getValue());
				// controller.calculateStateForCurrentTimeStep();
				controller.resetSimulation();
				if (running) {
					playBtn.doClick();
				}
			}
		});
		timeForwardBtn.setToolTipText("Forward");

		timeForwardBtn.setContentAreaFilled(false);
		timeForwardBtn.setBorder(null);
		timeForwardBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/forward.png",30,30)));
		timeForwardBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				timeSlider.setValue(timeSlider.getValue() + 1);
				controller.setCurIteration(timeSlider.getValue());
			}
		});
		timeBackwardBtn.setToolTipText("Backward");

		timeBackwardBtn.setBorder(null);
		timeBackwardBtn.setIcon(new ImageIcon(ImageImport.loadImage("/Button_Images/backward.png", 30,30)));
		timeBackwardBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				timeSlider.setValue(timeSlider.getValue() - 1);
				controller.setCurIteration(timeSlider.getValue());
			}
		});

		timeBtnPanel.add(playBtn);
		timeBtnPanel.add(Box.createRigidArea(new Dimension(10, 0)));
		timeBtnPanel.add(timeResetBtn);
		timeBtnPanel.add(Box.createRigidArea(new Dimension(10, 0)));
		timeBtnPanel.add(timeForwardBtn);

		// Speed Panel
		speedPanel.add(simSpeedLabel);
		speedPanel.add(speedSlider);
		speedSlider.setPaintTicks(true);
		speedSlider.setPaintLabels(true);
		speedSlider.setMaximum(6);
		speedSlider.setMinimum(0);
		speedSlider.setValue(1);
		
		speedSlider.setPaintLabels(true);
		Hashtable<Integer, JLabel> table = new Hashtable<Integer, JLabel>();
		table.put (0, new JLabel("1x"));
		table.put (1, new JLabel("2x"));
		table.put (2, new JLabel("4x"));
		table.put (3, new JLabel("8x"));
		table.put (4, new JLabel("16x"));
		table.put (5, new JLabel("32x"));
		table.put (6, new JLabel("64x"));
		    
		speedSlider.setLabelTable(table);
		
		speedSlider.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				/**
				 * Shifting Powers of two:
				 * e.g. 1<<0 -> 1 step per Second
				 *      1<<3 -> 8 steps per Second
				 *      and so on,
				 */
				int calculationsPerSecond = 1 << speedSlider.getValue();
				controller.setTimerSpeed(1024 >> speedSlider.getValue());
				speedSlider.setToolTipText("Speed: " + calculationsPerSecond + " Calculations per Second.");
			}
		});
		
		speedSlider.setToolTipText("Change the Number of Calculations per Secons");

		// Buttons and Speed Panel
		btnAndSpeedPanel.setLayout(new BorderLayout(0, 0));
		btnAndSpeedPanel.setBorder(null);
		btnAndSpeedPanel.add(timeBtnPanel, BorderLayout.NORTH);
		btnAndSpeedPanel.add(speedPanel, BorderLayout.CENTER);
		iterationsPanel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.anchor = GridBagConstraints.CENTER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		iterationsPanel.add(iterationsLabel, c);
		//iterationsLblHint=new LabelHint(iterationsLabel);
		hint.setForeground(Color.red);
		hint.setText(" ");
		iterationsField=new JTextField(6);//Considering hundreds of thousands in an extreme case
		iterationsField.setText(""+cont.getModel().getIterations());
		iterationsField.setToolTipText("0-" + MAX_ITERATIONS  );
		iterationsField.addActionListener(this);
		ScheduledThreadPoolExecutor s = new ScheduledThreadPoolExecutor(1);
		iterationsField.addCaretListener((e)->
			{
				try{
					iterationsField.setBackground(Color.WHITE);//red stings
					if(futureTask != null){
						futureTask.cancel(true);
					}
					futureTask = s.schedule((Runnable)() -> updateIterationsInput(), 1, TimeUnit.SECONDS);
					hint.setText(" ");
				}catch(NumberFormatException n){
					iterationsField.setBackground(PALE_RED);//red stings
					hint.setText("Invalid");
				}
			}
		);
		iterationsField.addFocusListener((interfaces.FocusListener)(e) ->{
			//FocusLost is not defined in interface have to be defined here
			updateIterationsInput();
		});
		c.gridy = 1;
		iterationsPanel.add(iterationsField, c);
		c.gridy = 2;
		iterationsPanel.add(hint, c);
		//iterationsPanel.add(new JLabel(), BorderLayout.SOUTH);
		timePanel.setLayout(new BorderLayout());;
		timePanel.add(iterationsPanel, BorderLayout.WEST);
		timePanel.add(timeSlider, BorderLayout.CENTER);
		this.add(btnAndSpeedPanel, BorderLayout.WEST);
		add(timePanel);
		
		//Disable Keys
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "UP_ARROW");
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "DOWN_ARROW");
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0, false), "PAGE_DOWN");
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0, false), "PAGE_UP");
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0, false), "END");
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0, false), "HOME");
		
		// Left arrow Key
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "LEFT_ARROW");
		timeSlider.getActionMap().put("LEFT_ARROW", new AbstractAction() {
			private static final long serialVersionUID = 1L;

			@Override
			public void actionPerformed(ActionEvent e) {
				timeSlider.setValue(timeSlider.getValue() - 1);
				controller.resetSimulation();
			}
		});
		
		//Right arrow Key
		timeSlider.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "Right_ARROW");
		timeSlider.getActionMap().put("Right_ARROW", new AbstractAction() {
			private static final long serialVersionUID = 1L;

			@Override
			public void actionPerformed(ActionEvent e) {
				timeSlider.setValue(timeSlider.getValue() + 1);
			}
		});
	}

	/**
	 * Returns the TimeSlider.
	 * 
	 * @return the TimeSlider
	 */
	public JSlider getTimeSlider() {
		return timeSlider;
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		updateIterationsInput();
	}
	/**
	 *  Update the Text field and apply valid changes to update Iterations from to model.
	 *  <p>
	 *  Executed by user input.
	 */
	private void updateIterationsInput() {
		try{
			int iterations=Integer.parseInt(iterationsField.getText());
			//iterationsLblHint.reset();
			boolean resetField=true;
			if(iterations<1)iterations=1;
			else if(iterations>MAX_ITERATIONS)iterations=MAX_ITERATIONS;
			else resetField=false;
			if(resetField)iterationsField.setText(""+iterations);
			controller.getModel().setIterations(Integer.parseInt(iterationsField.getText()));
			timeSlider.setMaximum(controller.getModel().getIterations()-1);
			timeSlider.setLabelTable(null);//Otherwise the ticks won't update
			timeSlider.setMajorTickSpacing((int)Math.ceil(((double)iterations)/20));
			timeSlider.setMinorTickSpacing((int)Math.ceil(((double)iterations)/100));//Even though the final mark can't actually be reached.
		}catch(NumberFormatException e){}
	}
}
