/*
	Copyright 2011 Daniel Pletea

	This file is part of T-Nano.

	T-Nano is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	T-Nano is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with T-Nano.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.metal.MetalBorders.ToolBarBorder;

/**
 * a class which it is the base for GuiAnalyze, GuiSimulation, GuiCircuit
 * 
 * @author Daniel Pletea
 * 
 */
public class Gui extends JPanel implements Definitions, ActionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// the toolbar variables
	private JToolBar toolbar;
	protected List<JButton> tools;
	protected List<BufferedImage> tiles;

	// the top menu variables
	private JMenu menu;
	private JMenuBar menuBar;

	// File menu
	protected JMenuItem newItem, openItem, saveAsItem, saveItem, exportItem,
			quitItem, closeTaskItem;

	// View menu
	protected JMenuItem zoomInItem, zoomOutItem, moveRight, moveLeft, moveUp,
			moveDown;
	protected JMenuItem theme1, theme2;

	// Edit menu
	protected JMenuItem undoItem, redoItem, selectModeItem, insertCompItem;

	// Tools menu
	protected JMenu toolsMenu;
	protected List<JMenuItem> toolsMenuItems;

	// Simulate menu
	JMenuItem runItem;

	// Analyse menu
	protected List<JMenuItem> methodItems;
	protected List<Integer> nrParamPerMethod;
	protected List<String> execNamePerMethod;

	// Help menu
	private JMenuItem helpItem;

	// the combobox where the user can switch between tasks
	protected static JComboBox taskComboBox;

	protected boolean paint;

	/**
	 * constructor
	 */
	public Gui() {
		initUI();
	}

	/**
	 * function that initiates different parts of the GUI
	 */
	protected void initUI() {
		this.setLayout(null);
		this.setBackground(BACKGROUND_COLOR);

		// instantiate the toolbar
		toolbar = new JToolBar();
		toolbar.setBounds(0, 30, 50, Main.fullSizeHeight - 50);
		toolbar.setRollover(true);
		toolbar.setOrientation(ToolBarBorder.VERTICAL);

		// fill the toolbar with ools
		createToolbar();

		// here is created the top menu
		createMenu();

		// here is created the taskcombo if it wasn't created before
		if (taskComboBox == null)
			createTaskComboBox();
		else {
			Main.guiList.get(Main.crt).remove(taskComboBox);
			this.add(taskComboBox);
		}

	}

	/**
	 * this function creates the taskbar
	 */
	protected void createTaskComboBox() {
		taskComboBox = new JComboBox();
		taskComboBox.setBounds(Main.fullSizeWidth - 350, 50, 300, 20);
		taskComboBox.addActionListener(this);

		// add toolbar to the panel
		this.add(taskComboBox);
	}

	/**
	 * function that create the top menu
	 */
	private void createMenu() {
		Singleton singleton = Singleton.getInstance();

		// Create the menu bar
		menuBar = new JMenuBar();

		// Build the File menu
		menu = new JMenu("File");
		menu.setMnemonic(KeyEvent.VK_A);

		// the items in the File menu
		newItem = singleton.createMenuItem("New", menu, this);
		newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				ActionEvent.CTRL_MASK));

		openItem = singleton.createMenuItem("Open", menu, this);
		openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
				ActionEvent.CTRL_MASK));

		saveAsItem = singleton.createMenuItem("Save as", menu, this);
		saveAsItem.setEnabled(false);

		saveItem = singleton.createMenuItem("Save", menu, this);
		saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
				ActionEvent.CTRL_MASK));
		saveItem.setEnabled(false);

		menu.addSeparator();

		exportItem = singleton.createMenuItem("Export", menu, this);
		exportItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
				ActionEvent.CTRL_MASK));
		exportItem.setEnabled(false);

		menu.addSeparator();

		closeTaskItem = singleton.createMenuItem("Close current task", menu,
				this);
		closeTaskItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W,
				ActionEvent.CTRL_MASK));
		closeTaskItem.setEnabled(false);

		quitItem = singleton.createMenuItem("Quit", menu, this);

		menuBar.add(menu);

		// Build the View menu
		menu = new JMenu("View");
		menu.setMnemonic(KeyEvent.VK_B);

		// create the menuItems that are part of this View menus
		zoomInItem = singleton.createMenuItem("Zoom in", menu, this);
		zoomInItem.setAccelerator(KeyStroke.getKeyStroke('='));
		zoomInItem.setEnabled(false);

		zoomOutItem = singleton.createMenuItem("Zoom out", menu, this);
		zoomOutItem.setAccelerator(KeyStroke.getKeyStroke('-'));
		zoomOutItem.setEnabled(false);

		menu.addSeparator();

		moveLeft = singleton.createMenuItem("Move left", menu, this);
		moveLeft.setAccelerator(KeyStroke.getKeyStroke('a'));
		moveLeft.setEnabled(false);

		moveRight = singleton.createMenuItem("Move right", menu, this);
		moveRight.setAccelerator(KeyStroke.getKeyStroke('d'));
		moveRight.setEnabled(false);

		moveUp = singleton.createMenuItem("Move up", menu, this);
		moveUp.setAccelerator(KeyStroke.getKeyStroke('w'));
		moveUp.setEnabled(false);

		moveDown = singleton.createMenuItem("Move down", menu, this);
		moveDown.setAccelerator(KeyStroke.getKeyStroke('s'));
		moveDown.setEnabled(false);

		menu.addSeparator();

		JMenu themeMenu = new JMenu("Choose theme");

		theme1 = singleton.createMenuItem("Gray/White theme", themeMenu, this);
		theme1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1,
				ActionEvent.CTRL_MASK));
		theme1.setEnabled(false);

		theme2 = singleton.createMenuItem("DNA theme", themeMenu, this);
		theme2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2,
				ActionEvent.CTRL_MASK));
		theme2.setEnabled(false);

		menu.add(themeMenu);
		menuBar.add(menu);

		// Build the Edit menu
		menu = new JMenu("Edit");
		menu.setMnemonic(KeyEvent.VK_C);

		undoItem = singleton.createMenuItem("Undo", menu, this);
		undoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z,
				ActionEvent.CTRL_MASK));
		undoItem.setEnabled(false);

		redoItem = singleton.createMenuItem("Redo", menu, this);
		redoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
				ActionEvent.CTRL_MASK));
		redoItem.setEnabled(false);

		selectModeItem = singleton.createMenuItem("Click mode", menu, this);
		selectModeItem.setAccelerator(KeyStroke.getKeyStroke('c'));
		selectModeItem.setEnabled(false);

		insertCompItem = singleton.createMenuItem("Insert component", menu,
				this);
		insertCompItem.setAccelerator(KeyStroke.getKeyStroke('i'));
		insertCompItem.setEnabled(false);

		menuBar.add(menu);

		// Build the Tools menu
		toolsMenu = new JMenu("Tools");
		toolsMenu.setMnemonic(KeyEvent.VK_D);

		// adding the toolsItems previous created on the creation of the toolbar
		createToolsMenu();
		menuBar.add(toolsMenu);

		// Build the Analyze menu
		menu = new JMenu("Analyze");
		menu.setMnemonic(KeyEvent.VK_E);

		methodItems = new ArrayList<JMenuItem>();
		execNamePerMethod = new ArrayList<String>();
		nrParamPerMethod = new ArrayList<Integer>();

		// adding the fault tolerance method
		createMethod("Fault Tolerance", "runtest", 3);
		// here can be added more and more type of analyzing

		menuBar.add(menu);

		// Build the Snalyze menu
		menu = new JMenu("Simulate");
		menu.setMnemonic(KeyEvent.VK_F);

		runItem = singleton.createMenuItem("Run", menu, this);
		runItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
				ActionEvent.CTRL_MASK));
		runItem.setEnabled(false);

		menuBar.add(menu);

		// Build the Help menu
		menu = new JMenu("Help");
		menu.setMnemonic(KeyEvent.VK_G);

		helpItem = singleton.createMenuItem("Readme", menu, this);

		menuBar.add(menu);

		menuBar.setBounds(0, 0, Main.fullSizeWidth, 30);
		this.add(menuBar);
	}

	/**
	 * this the magic functions that creates a new method like the
	 * FaultTolerance
	 * 
	 * @param menuName
	 * @param execName
	 * @param nrParameters
	 *            = nrParameters of the program - 3 ( exec, inputFile, ....,
	 *            progressFile )
	 */
	public void createMethod(String menuName, String execName, int nrParameters) {
		JMenuItem methodItem = Singleton.getInstance().createMenuItem(menuName,
				menu, this);
		methodItem.setEnabled(false);

		methodItems.add(methodItem);
		execNamePerMethod.add(execName);
		nrParamPerMethod.add(nrParameters);
	}

	/**
	 * function which creates the toolsMenu
	 */
	public void createToolsMenu() {
		if (toolsMenu != null)
			toolsMenu.removeAll();

		// adding the blank tile
		addToolsToMenu("Blank tile", toolsMenu, 0, 1);

		// adding the wired tiles
		addToolsToMenu("Wired tiles", toolsMenu, 3, 15);

		// adding the transistors
		addToolsToMenu("Transistor tiles", toolsMenu, 1, 3);

		// adding the voltage tiles
		addToolsToMenu("Voltage tiles", toolsMenu, 15, 17);

		// adding the IO tiles
		addToolsToMenu("IO tiles", toolsMenu, 17, 19);
	}

	/**
	 * this function adds to the menu given as parameter another menu with the
	 * name given as first parameter this menu added ( auxMenu ) is populated
	 * with tools from the list toolsMenuItems
	 * 
	 * @param string
	 * @param menu
	 * @param start
	 * @param stop
	 */
	private void addToolsToMenu(String string, JMenu menu, int start, int stop) {
		JMenu auxMenu = new JMenu(string);
		for (int i = start; i < stop; i++)
			auxMenu.add(toolsMenuItems.get(i));
		menu.add(auxMenu);
	}

	/**
	 * this function creates the toolbar
	 */
	public void createToolbar() {
		Image im;

		tiles = Singleton.getInstance().createTiles();

		// clear the toolbar if necessary
		if (toolbar != null)
			toolbar.removeAll();

		//
		tools = new ArrayList<JButton>();
		toolsMenuItems = new ArrayList<JMenuItem>();

		// add to the toolbar all 15 tools
		for (int i = 0; i < NR_TILES - 1; i++) {
			// scale the image
			im = tiles.get(i).getScaledInstance(25, 25,
					BufferedImage.SCALE_DEFAULT);

			// create button for tool
			JButton button = new JButton(new ImageIcon(im));
			button.addActionListener(this);

			// add the tool to the toolbar
			toolbar.add(button);
			toolbar.addSeparator();
			tools.add(button);

			JMenuItem menuItem = new JMenuItem(new ImageIcon(im));
			menuItem.addActionListener(this);

			// add the tool to the tools
			toolsMenuItems.add(menuItem);
		}

		// add toolbar to the panel
		this.add(toolbar);

	}

	@Override
	public void actionPerformed(ActionEvent e) {

		JComponent source = (JComponent) e.getSource();

		// create a new circuit
		if (source.equals(newItem)) {
			System.out.println("New circuit creating");

			// create a dialog and ask user about the dimensions of the circuit
			// he wants to create
			NewFileDialog newFileDialog = new NewFileDialog();
			newFileDialog.setVisible(true);
			if (newFileDialog.hasData()) {
				List<Integer> dimensions = newFileDialog.getData();
				createTask(dimensions.get(1), dimensions.get(0));
			}
		} else if (source.equals(openItem)) {
			System.out.println("Open");

			String fileName = openPopupWindow();
			if (fileName != null) {
				try {
					BufferedReader in = new BufferedReader(new FileReader(
							fileName));
					createTask(fileName);
					in.close();
				} catch (FileNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}

		} else if (source.equals(taskComboBox)) {
			// remove the taskbar from the crt gui
			Main.guiList.get(Main.crt).remove(taskComboBox);

			// set the crt gui
			Main.crt = taskComboBox.getSelectedIndex() + 1;
			(Main.guiList.get(Main.crt)).signal();
			Main.x = true;

			// put the taskbar in other
			Main.guiList.get(Main.crt).add(taskComboBox);
		} else if (source.equals(quitItem)) {

			// exiting
			System.exit(0);

		} else if (source.equals(closeTaskItem)) {
			System.out.println("Close current task action " + Main.crt);

			// remove this task from the program
			Main.crt--;
			taskComboBox.removeItemAt(taskComboBox.getSelectedIndex());
			Main.guiList.remove(Main.crt + 1);

			// repaint
			paint = true;
			Main.x = true;
		} else if (source.equals(helpItem)) {
			createHelp();
		}

	}

	protected String openPopupWindow() {
		JFileChooser chooser = new JFileChooser();
		chooser.setCurrentDirectory(new File("."));
		chooser.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				return f.getName().toLowerCase().endsWith(".tnn")
						|| f.isDirectory();
			}

			public String getDescription() {
				return "TNN Files";
			}
		});
		int r = chooser.showOpenDialog(this);
		if (r == JFileChooser.APPROVE_OPTION)
			return chooser.getSelectedFile().getPath();
		else
			return null;
	}

	private void createTask(int circuitWidth, int circuitHeight) {
		System.out.println("Create task action");
		Main.guiList.add(new GuiCircuit(circuitWidth, circuitHeight));
		updateMain("Untitled" + Singleton.getInstance().getNrUntitled());
		Singleton.getInstance().setNrUntitled(
				Singleton.getInstance().getNrUntitled() + 1);
	}

	private void createTask(String fileName) {
		System.out.println("Create task action");
		Main.guiList.add(new GuiCircuit(fileName));
		updateMain(Singleton.getInstance().tranformName(fileName));
	}
	
	private void createHelp() {
		System.out.println("Create help");
		Main.guiList.add(new GuiHelp());
		updateMain("help");
	}

	private void updateMain(String taskName) {
		Main.crt = Main.guiList.size() - 1;
		Main.x = true;

		taskComboBox.addItem(taskName);
		taskComboBox.setSelectedIndex(taskComboBox.getItemCount() - 1);

		// feedback
		System.out.println("Main updated");
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
	}

	public void update() {
		if (paint) {
			repaint();
			paint = false;
		}
	}

	protected void signal() {
		paint = true;
	}
}
