import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.Border;

public class Interface extends JFrame implements ActionListener, FocusListener {
	private static final long serialVersionUID = 1L;
	private static final Font titleFont = new Font(new JLabel().getFont().getName(), Font.BOLD, 14);
	private static final Font defaultFont = new Font(new JLabel().getFont().getName(), Font.PLAIN, new JLabel().getFont().getSize());
	private static final Border defaultJTextFieldBorder = new JTextField().getBorder();
	
	private JTextArea logs;
	private JButton[][] moveButtons = new JButton[3][3];
	private JTextField stepsField;
	private JButton waitButton;
	private JButton nextButton;
	private JButton revertButton;
	private JButton exportButton;
	private JButton importButton;
	private boolean keyboardWait = false;
	
	private ActionList actionList;

	public Interface(ActionList actionList, final Runnable executeOnClose) {
		super("PercTeacher");
		this.actionList = actionList;
		setResizable(false);
		populateWindow();
		pack();
		setLocationRelativeTo(null); // center window
		
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
        	.addKeyEventDispatcher(new InterfaceKeyboardDispatcher());
        getContentPane().requestFocusInWindow();
        
        addWindowListener(new WindowAdapter() {
        	@Override
        	public void windowClosing(WindowEvent e) {
        		if(executeOnClose != null)
        			executeOnClose.run();
        		dispose();
        	}
		});
	}
	
	private void populateWindow() {
		JPanel mainPanel = new JPanel(new BorderLayout());
		addLogPanel(mainPanel);
		
		JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
		addMoveCommandsPanel(centerPanel);
		JPanel optionCommandsPanel = new JPanel();
		addOptionCommandsPanel(centerPanel, optionCommandsPanel);
		addExportImportPanel(optionCommandsPanel);
		
		mainPanel.add(centerPanel, BorderLayout.CENTER);
		add(mainPanel);
		
		updateLogs();
	}
	
	private void addLogPanel(JPanel parent) {
		JPanel logPanel = new JPanel(new BorderLayout());
		logPanel.setBorder(BorderFactory.createMatteBorder(2, 0, 0, 0, Color.LIGHT_GRAY));
		
		JLabel loglabel = new JLabel("Actions");
		loglabel.setFont(titleFont);
		logPanel.add(loglabel, BorderLayout.NORTH);

		logs = new JTextArea(6, 0);
		logs.setEditable(false);
		JScrollPane logScroll = new JScrollPane(logs, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		logPanel.add(logScroll, BorderLayout.CENTER);
		
		parent.add(logPanel, BorderLayout.SOUTH);
	}
	
	private void addMoveCommandsPanel(JPanel parent) {
		JPanel moveCommandsPanel = new JPanel(new GridLayout(3, 3));
		moveCommandsPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 2, Color.LIGHT_GRAY));
		
		for(int i=0; i<3; i++) {
			for(int j=0; j<3; j++) {
				if(i == 1 && j == 1) { // central component is an image of the robot
					JLabel label = new JLabel(new ImageIcon(getClass().getResource("/icons/robot.png")));
					moveButtons[i][j] = null;
					moveCommandsPanel.add(label);
				} else {
					JButton button = new JButton(new ImageIcon(getClass().getResource("/icons/move" + String.valueOf(j) + String.valueOf(i) + ".png")));
					button.addActionListener(this);
					moveButtons[i][j] = button;
					moveCommandsPanel.add(button);
				}
			} 	
		}

		parent.add(moveCommandsPanel, BorderLayout.CENTER);
	}
	
	private void addOptionCommandsPanel(JPanel parent, JPanel optionCommandsPanel) {
		optionCommandsPanel.setLayout(new BoxLayout(optionCommandsPanel, BoxLayout.PAGE_AXIS));
		
		// title
		JLabel title = new JLabel("Options");
		title.setFont(titleFont);
		title.setAlignmentX(Component.LEFT_ALIGNMENT);
		optionCommandsPanel.add(title);
		
		// steps
		JPanel stepPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
		stepPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		
		JLabel stepLabel = new JLabel("Steps : ");
		stepLabel.setFont(defaultFont);
		stepPanel.add(stepLabel);
		
		stepsField = new JTextField(10);
		stepsField.setText("10");
		stepsField.addFocusListener(this);
		stepPanel.add(stepsField);
		
		optionCommandsPanel.add(stepPanel);

		//buttons
		JPanel buttonsPanel = new JPanel(new GridLayout(0, 1));
		buttonsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		
		// wait button
		waitButton = new JButton("Add a pause");
		waitButton.addActionListener(this);
		buttonsPanel.add(waitButton);

		// next action button
		nextButton = new JButton("Next action");
		nextButton.addActionListener(this);
		buttonsPanel.add(nextButton);

		// revert action button
		revertButton = new JButton("Revert last action");
		revertButton.addActionListener(this);
		buttonsPanel.add(revertButton);
		
		optionCommandsPanel.add(buttonsPanel);
		
		// separator
		JSeparator sep = new JSeparator();
		sep.setForeground(Color.LIGHT_GRAY);
		sep.setBackground(Color.LIGHT_GRAY);
		optionCommandsPanel.add(sep);
		
		parent.add(optionCommandsPanel, BorderLayout.EAST);
	}
	
	private void addExportImportPanel(JPanel parent) {
		JPanel exportImportPanel = new JPanel(new GridLayout(0, 1));
		exportImportPanel.setAlignmentX(Component.LEFT_ALIGNMENT);

		// export
		exportButton = new JButton("Export actions");
		exportButton.addActionListener(this);
		exportImportPanel.add(exportButton);
		
		// import
		importButton = new JButton("Import actions");
		importButton.addActionListener(this);
		exportImportPanel.add(importButton);
		
		parent.add(exportImportPanel);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		keyboardWait = true;
		for(int i=0; i<3; i++) {
			for(int j=0; j<3; j++) {
				if(e.getSource() == moveButtons[i][j]) {
					String str = stepsField.getText();
					try {
						if(str != null && !str.equals("")) {
							int step = Integer.parseInt(str);
							actionList.addToActionList(i, j, step);
						}
					} catch (NumberFormatException ignored) {
						stepsField.setBorder(BorderFactory.createLineBorder(Color.RED));
					}
				}
			}
		}
		if(e.getSource() == waitButton) {
			String str = JOptionPane.showInputDialog(null, "Pause duration (in miliseconds) :");
			try {
				if(str != null && !str.equals("")) {
					long delai = Long.parseLong(str) * 1000;
					actionList.completeActionAndAddPause(delai);
				}
			} catch (NumberFormatException ignored) {}
		} else if(e.getSource() == nextButton) {
			actionList.completeAction();
		} else if(e.getSource() == revertButton) {
			actionList.revertLastAction();
		} else if(e.getSource() == exportButton) {
			ImportExport.exportWithDialog(actionList);
		} else if(e.getSource() == importButton) {
			ActionList newActionList = ImportExport.importsWithDialog();
			if(newActionList != null)
				actionList = newActionList;
		}
		updateLogs();
		keyboardWait = false;
	}
	
	private void updateLogs() {
		if(actionList != null)
			logs.setText(actionList.toString());
	}

	@Override
	public void focusGained(FocusEvent e) {
		keyboardWait = true;
		if(e.getSource() == stepsField) {
			stepsField.setBorder(defaultJTextFieldBorder);
			stepsField.setCaretPosition(stepsField.getDocument().getLength());
		}
	}
	@Override
	public void focusLost(FocusEvent e) {
		keyboardWait = false;
	}
	
	private class InterfaceKeyboardDispatcher implements KeyEventDispatcher {
		@Override
		public boolean dispatchKeyEvent(KeyEvent e) {
			if(keyboardWait && Interface.this.getFocusOwner() == stepsField && e.getKeyCode() == KeyEvent.VK_ENTER) {
				Interface.this.getContentPane().requestFocusInWindow();
				return true;
			}
			if(e.getID() != KeyEvent.KEY_RELEASED || keyboardWait)
				return false;
			if(e.getKeyCode() == KeyEvent.VK_NUMPAD1)
				actionPerformed(new ActionEvent(moveButtons[2][0], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD2)
				actionPerformed(new ActionEvent(moveButtons[2][1], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD3)
				actionPerformed(new ActionEvent(moveButtons[2][2], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD4)
				actionPerformed(new ActionEvent(moveButtons[1][0], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD6)
				actionPerformed(new ActionEvent(moveButtons[1][2], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD7)
				actionPerformed(new ActionEvent(moveButtons[0][0], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD8)
				actionPerformed(new ActionEvent(moveButtons[0][1], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD9)
				actionPerformed(new ActionEvent(moveButtons[0][2], 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_SPACE)
				actionPerformed(new ActionEvent(nextButton, 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE)
				actionPerformed(new ActionEvent(revertButton, 0, null));
			else if(e.getKeyCode() == KeyEvent.VK_NUMPAD0)
				actionPerformed(new ActionEvent(waitButton, 0, null));
			return true;
		}
	}
}
