/*
 * @(#)BWFrame.java
 */
package ereinionbw;

import java.awt.Button;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.Frame;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Image;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.MenuItem;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuShortcut;
import java.awt.CheckboxMenuItem;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.util.Properties;
import java.util.Vector;
import java.util.Enumeration;

import ereinion.awt.CloseableFrame;
import ereinion.awt.CheckboxMIGroup;
import ereinion.awt.AWTSetter;
import ereinion.awt.AWTBaseSetter;
import ereinion.awt.Texter;
import ereinion.search.SearchExceptionFail;
import ereinion.search.SearchStats;
import ereinion.search.SearchNode;
import ereinion.search.SearchState;
import ereinion.search.SearchOp;
import ereinion.search.SearchProblem;


/*
 * classe di package : BWFrame
 *
 * La finestra principale del programma.
 */
class BWFrame extends Frame implements WindowListener, ActionListener, ItemListener
{

	public BWFrame(BWData data, AWTSetter plain, AWTSetter bold, int xDim)
	{
		super("Blue Wizard v 1.02 by Ereinion");

		awtData = data;

		searchHistory = new Vector();
		searchProp = new Properties();

		otherFrame = null;

		xDimension = xDim;
		
		setMedia(this);

		mainSetter = plain;
		mainSetterBold = bold;

		mainSetter.setComponent(this);

		problemLoader = new BWLoader(mainSetter,xDimension);

		solvingAgent = new BWSolver();
		int depthIndex = 1;
		int timeIndex = 1;
		int searchIndex = 0;
		int expIndex = 0;
		solvingAgent.setCurrentTimeOut(TIMEOUT[timeIndex]*1000);
		solvingAgent.setCurrentDepth(DEPTH[depthIndex]);
		solvingAgent.setExpansionType(expIndex);
		solvingAgent.setSearchType(searchIndex);

		// menu layout
		menuBar = new MenuBar();
		actionMenu = (Menu)mainSetter.setMenuItem(new Menu("Actions"));
		actionMI = new MenuItem[ACTIONS.length];
		for (int k=0; k<ACTIONS.length; k++) {
			actionMI[k] = (MenuItem)mainSetter.setMenuItem(new MenuItem(ACTIONS[k],new MenuShortcut(ACTIONS_KEY[k])),actionMenu);
			actionMI[k].addActionListener(this);
		}
		menuBar.add(actionMenu);
		problemMenu = (Menu)mainSetter.setMenuItem(new Menu("Problem"));
		problems = problemLoader.getProblemList();
		problemMI = new CheckboxMenuItem[problems.length];
		CheckboxMIGroup problemGroup = new CheckboxMIGroup();
		currentProblemIndex = 0;
		currentProblem = problems[currentProblemIndex];
		for (int k=0; k<problems.length; k++) {
			String name = problems[k].getProblemName();
			problemMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(problemGroup.makeBox(name,false),problemMenu);
			problemMI[k].addItemListener(this);
		}
		problemMI[currentProblemIndex].setState(true);
		menuBar.add(problemMenu);
		searchMenu = (Menu)mainSetter.setMenuItem(new Menu("Search"));
		searchMI = new CheckboxMenuItem[BWSolver.SEARCH_MENU.length];
		CheckboxMIGroup searchGroup = new CheckboxMIGroup();
		for (int k=0; k<BWSolver.SEARCH_MENU.length; k++) {
			searchMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(searchGroup.makeBox(BWSolver.SEARCH_MENU[k],false),searchMenu);
			searchMI[k].addItemListener(this);
		}
		searchMI[searchIndex].setState(true);
		menuBar.add(searchMenu);
		timeOutMenu = (Menu)mainSetter.setMenuItem(new Menu("Time Out"));
		timeOutMI = new CheckboxMenuItem[TIMEOUT.length];
		CheckboxMIGroup timeOutGroup = new CheckboxMIGroup();
		for (int k=0; k<TIMEOUT.length; k++) {
			timeOutMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(timeOutGroup.makeBox(""+TIMEOUT[k]+" seconds",false),timeOutMenu);
			timeOutMI[k].addItemListener(this);
		}
		timeOutMI[timeIndex].setState(true);
		menuBar.add(timeOutMenu);
		expansionMenu = (Menu)mainSetter.setMenuItem(new Menu("Expansion"));
		expansionMI = new CheckboxMenuItem[BWSolver.EXP_MENU.length];
		CheckboxMIGroup expansionGroup = new CheckboxMIGroup();
		for (int k=0; k<BWSolver.EXP_MENU.length; k++) {
			expansionMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(expansionGroup.makeBox(BWSolver.EXP_MENU[k],false),expansionMenu);
			expansionMI[k].addItemListener(this);
		}
		expansionMI[expIndex].setState(true);
		menuBar.add(expansionMenu);
		depthLimitMenu = (Menu)mainSetter.setMenuItem(new Menu("Depth Limit"));
		depthLimitMI = new CheckboxMenuItem[DEPTH.length];
		CheckboxMIGroup depthLimitGroup = new CheckboxMIGroup();
		for (int k=0; k<TIMEOUT.length; k++) {
			depthLimitMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(depthLimitGroup.makeBox(""+DEPTH[k],false),depthLimitMenu);
			depthLimitMI[k].addItemListener(this);
		}
		depthLimitMI[depthIndex].setState(true);
		menuBar.add(depthLimitMenu);
		helpMenu = (Menu)mainSetter.setMenuItem(new Menu("?"));
		helpMI = new MenuItem[1];
		helpMI[0] = (MenuItem)mainSetter.setMenuItem(new MenuItem("help"),helpMenu);
		helpMI[0].addActionListener(this);
		menuBar.setHelpMenu(helpMenu);
		setMenuBar(menuBar);

		// component layout
		int dis = 2;
		Panel framePanel = (Panel)mainSetter.setComponent(new Panel(new BorderLayout()));
		messageLabel = (Label)mainSetter.setComponent(new Label("Blue Wizard Messages",Label.CENTER));
		framePanel.add(messageLabel,BorderLayout.SOUTH);
		// main panel
		mainPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(1,2,dis,dis)));
		// left

		leftPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(2,1,dis,dis)));
		problemDataPanel = (Panel)mainSetter.setComponent(new Panel(new BorderLayout()));
		problemNameLabel = (Label)mainSetter.setComponent(new Label("Name",Label.CENTER));
		problemNameLabel.setText(currentProblem.getProblemName());
		problemDescriptionArea = (Texter)mainSetter.setComponent(new Texter());
		problemDataPanel.add(problemNameLabel,BorderLayout.NORTH);
		problemDataPanel.add(problemDescriptionArea,BorderLayout.CENTER);
		// search control panel
		Panel searchControlPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(3,1,dis,dis)));
		actionButton = new Button[ACTIONS.length];
		Panel actionButtonPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(4,2)));
		mainSetterBold.setComponent(new Label(""),actionButtonPanel);
		mainSetterBold.setComponent(new Label(""),actionButtonPanel);
		mainSetterBold.setComponent(new Label("Action Buttons",Label.CENTER),actionButtonPanel);
		mainSetterBold.setComponent(new Label(""),actionButtonPanel);
		actionButton[0] = (Button)mainSetter.setComponent(new Button(ACTIONS[0]),actionButtonPanel);
		mainSetterBold.setComponent(new Label("<-- Search problem solution!",Label.LEFT),actionButtonPanel);
		actionButton[1] = (Button)mainSetter.setComponent(new Button(ACTIONS[1]),actionButtonPanel);
		mainSetterBold.setComponent(new Label("<-- Show Search Tree!",Label.LEFT),actionButtonPanel);
		Panel frameButtonPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(4,2)));
		actionButton[2] = (Button)mainSetter.setComponent(new Button(ACTIONS[2]),frameButtonPanel);
		mainSetterBold.setComponent(new Label("<-- Show last solution found!",Label.LEFT),frameButtonPanel);
		actionButton[3] = (Button)mainSetter.setComponent(new Button(ACTIONS[3]),frameButtonPanel);
		mainSetterBold.setComponent(new Label("<-- Config current problem!",Label.LEFT),frameButtonPanel);
		actionButton[4] = (Button)mainSetter.setComponent(new Button(ACTIONS[4]),frameButtonPanel);
		mainSetterBold.setComponent(new Label("<-- Show previous search data!",Label.LEFT),frameButtonPanel);
		mainSetterBold.setComponent(new Label("",Label.LEFT),frameButtonPanel);
		mainSetterBold.setComponent(new Label("",Label.LEFT),frameButtonPanel);
		for (int k=0; k<ACTIONS.length; k++)
			actionButton[k].addActionListener(this);
		Panel searchConfigPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(7,4)));
		mainSetter.setComponent(new Label(),searchConfigPanel);
		mainSetter.setComponent(new Label(),searchConfigPanel);
		mainSetterBold.setComponent(new Label(">> Search ",Label.RIGHT),searchConfigPanel);
		mainSetterBold.setComponent(new Label("Data <<",Label.LEFT),searchConfigPanel);
		mainSetter.setComponent(new Label("Search: ",Label.RIGHT),searchConfigPanel);
		searchType = (Label)mainSetter.setComponent(new Label(searchMI[searchIndex].getLabel(),Label.LEFT),searchConfigPanel);
		mainSetter.setComponent(new Label("TimeOut: ",Label.RIGHT),searchConfigPanel);
		searchTimeOut = (Label)mainSetter.setComponent(new Label(timeOutMI[timeIndex].getLabel(),Label.LEFT),searchConfigPanel);
		mainSetter.setComponent(new Label("Expansion: ",Label.RIGHT),searchConfigPanel);
		searchExpansion = (Label)mainSetter.setComponent(new Label(expansionMI[expIndex].getLabel(),Label.LEFT),searchConfigPanel);
		mainSetter.setComponent(new Label("Depth: ",Label.RIGHT),searchConfigPanel);
		searchDepth = (Label)mainSetter.setComponent(new Label(depthLimitMI[depthIndex].getLabel(),Label.LEFT),searchConfigPanel);
		mainSetter.setComponent(new Label(),searchConfigPanel);
		mainSetter.setComponent(new Label(),searchConfigPanel);
		searchControlPanel.add(actionButtonPanel);
		searchControlPanel.add(frameButtonPanel);
		searchControlPanel.add(searchConfigPanel);
		// search data panel
		leftPanel.add(searchControlPanel);
		leftPanel.add(problemDataPanel);
		// right
		rightPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(2,1,dis,dis)));
		statsPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(BWSolver.STATS.length,2,dis,dis)));
		statsLabel = new IndicatorLabel[BWSolver.STATS.length];
		for (int k=0; k<BWSolver.STATS.length; k++) {
			mainSetter.setComponent(new Label(BWSolver.STATS[k],Label.CENTER),statsPanel);
			int seed = 10;
			statsLabel[k] = (IndicatorLabel)mainSetter.setComponent(new IndicatorLabel(indColor,seed),statsPanel);
		}
		logoCanvas = (ImageCanvas)mainSetter.setComponent(new ImageCanvas(awtData.logoImage));
		rightPanel.add(logoCanvas);
		rightPanel.add(statsPanel);
		mainPanel.add(leftPanel);
		mainPanel.add(rightPanel);
		framePanel.add(mainPanel,BorderLayout.CENTER);

		currentProblemIndex = -1;
		changeProblem(0);

		stats = new SearchStatsTree(0,100);

		//last settings
		setLayout(new GridLayout(1,1));
		add(framePanel);
		addWindowListener(this);
		setSize(xDimension,xDimension*3/5);
		setResizable(false);
		show();
		toFront();

	}

	/*** event handling ***/

	private void setMedia(Frame f)
	{
		if (awtData.normalCursor != null)
			f.setCursor(awtData.normalCursor);
		if (awtData.iconImage != null)
			f.setIconImage(awtData.iconImage);
	}

	private void switchCursor()
	{

	}

	private void changeTimeOut(int index)
	{

		String value = timeOutMI[index].getLabel();
		solvingAgent.setCurrentTimeOut(TIMEOUT[index]*1000);
		searchTimeOut.setText(value);
	}

	private void changeDepthLimit(int index)
	{
		String value = depthLimitMI[index].getLabel();
		solvingAgent.setCurrentDepth(DEPTH[index]);
		searchDepth.setText(value);
	}

	private void changeExpansion(int type)
	{
		String value = expansionMI[type].getLabel();
		solvingAgent.setExpansionType(type);
		searchExpansion.setText(value);
	}

	private void changeSearch(int type)
	{
		solvingAgent.setSearchType(type);
		String value = searchMI[type].getLabel();
		searchType.setText(value);
	}

	private void changeProblem(int index)
	{
		if (index != currentProblemIndex) {
			currentProblemIndex = index;
			currentProblem = problems[index];
			String name = problemMI[index].getLabel();
			problemDescriptionArea.displayText(currentProblem.getProblemDescription());
			problemNameLabel.setText(name);
		}
	}

	public void itemStateChanged(ItemEvent e)
	{
		Object obj = e.getSource();
		for (int k=0; k<searchMI.length; k++)
			if (searchMI[k] == obj)
				changeSearch(k);
		for (int k=0; k<timeOutMI.length; k++)
			if (timeOutMI[k] == obj)
				changeTimeOut(k);
		for (int k=0; k<expansionMI.length; k++)
			if (expansionMI[k] == obj)
				changeExpansion(k);
		for (int k=0; k<depthLimitMI.length; k++)
			if (depthLimitMI[k] == obj)
				changeDepthLimit(k);
		for (int k=0; k<problemMI.length; k++)
			if (problemMI[k] == obj)
				changeProblem(k);

	}

	private void handleSearchSolution()
	{
		try {
			stats = new SearchStatsTree(0,100);
			long baseTime = System.currentTimeMillis();
			try  {
				try {
					messageLabel.setText("Search Started...");
					searchProp.put(BWHistory.P_SEARCH,searchType.getText());
					searchProp.put(BWHistory.P_PROBLEM,problemNameLabel.getText());
					searchProp.put(BWHistory.P_EXP,searchExpansion.getText());
					solutionFound = null;
					try {
						solutionFound = solvingAgent.searchSolution(currentProblem,stats);
					} catch (ProblemNotInformed pni) { messageLabel.setText("No eurstics available for this problem!"); }
					baseTime = System.currentTimeMillis()-baseTime;
					handleResult("Search Complete: Success!",(int)baseTime);
				} catch (SearchExceptionFail fail) {
					String result = "Fail";
					if (fail.getFailType() == SearchExceptionFail.FAIL_TIMEOUT)
						result = "Time Out";
					else if (fail.getFailType() == SearchExceptionFail.FAIL_CUT)
						result = "Cut (Depth Limit Reached)";
					else if (fail.getFailType() == SearchExceptionFail.FAIL_NOP)
						result = "Fail: No Valid Operator Avaible";
					handleResult(result,(int)baseTime);
				}
			} catch (OutOfMemoryError oom) {
				handleResult("Out Of Memory: "+stats.getMaximumSpace()+" nodes",(int)baseTime);
			}
		} catch (Exception e) {
			messageLabel.setText("Unespected Exception: "+e.getMessage());
		}
	}

	private void handleResult(String result, int time)
	{
		messageLabel.setText(result);
		searchProp.put(BWHistory.P_RESULT,result);
		statsLabel[0].setValue(time);
		statsLabel[1].setValue(stats.getMaximumSpace());
		statsLabel[2].setValue(stats.getMaximumDepth());
		statsLabel[3].setValue(stats.getExpandedCount());
		searchProp.put(BWHistory.P_STATS[0],""+time);
		searchProp.put(BWHistory.P_STATS[1],""+stats.getMaximumSpace());
		searchProp.put(BWHistory.P_STATS[2],""+stats.getMaximumDepth());
		searchProp.put(BWHistory.P_STATS[3],""+stats.getExpandedCount());
		if (solutionFound != null) {
			statsLabel[4].setValue(solutionFound.getDepth());
			statsLabel[5].setValue(solutionFound.getPathCost());
			searchProp.put(BWHistory.P_STATS[4],""+solutionFound.getDepth());
			searchProp.put(BWHistory.P_STATS[5],""+solutionFound.getPathCost());
		} else {
			statsLabel[4].setValue(0);
			statsLabel[5].setValue(0);
			searchProp.put(BWHistory.P_STATS[4],"0");
			searchProp.put(BWHistory.P_STATS[5],"0");
		}
		searchProp.put(BWHistory.P_RESULT,messageLabel.getText());
		Properties tmp = new Properties();
		Enumeration elem = searchProp.keys();
		while (elem.hasMoreElements()) {
			String key = (String)elem.nextElement();
			tmp.put(key,searchProp.getProperty(key));
		}
		searchHistory.insertElementAt(searchProp,0);
		searchProp = tmp;
	}

	private void handleConfigProblem()
	{
		hide();
		otherFrame = currentProblem.getConfigFrame();
		otherFrame.addWindowListener(this);
		setMedia(otherFrame);
	}

	private void handleShowSolution()
	{
		if (solutionFound != null) {
			hide();
			otherFrame = new SearchSolutionFrame(mainSetter,currentProblem,solutionFound,currentProblem.getStateDisplayer(),xDimension);
			otherFrame.addWindowListener(this);
			setMedia(otherFrame);
		} else
			messageLabel.setText("No Solution Found");
	}

	private void handleHelp()
	{
		hide();
		Texter helpTexter = new Texter();
		helpTexter.setText(awtData.helpText);
		otherFrame = new CloseableFrame("Blue Wizard Help Frame",helpTexter);
		mainSetter.setComponent(helpTexter);
		mainSetter.setComponent(otherFrame);
		otherFrame.addWindowListener(this);
		otherFrame.setSize(xDimension,xDimension/2);
		otherFrame.show();
		setMedia(otherFrame);
	}

	private void handleShowHistory()
	{
		hide();
		otherFrame = new BWHistory(mainSetter,xDimension,searchHistory,indColor);
		otherFrame.addWindowListener(this);
		setMedia(otherFrame);
	}

	private void handleTreeFrame()
	{
		SearchNode[] nodes = stats.getNodes();
		if (nodes.length == 0)
			messageLabel.setText("No Solution Found");
		else {
			hide();
			Texter treeTexter = new Texter();
			BWTreeNode[][] map = (new BWTree(nodes)).getTreeMap();
			String text = new String();
			for (int i=0; i<map.length; i++) {
				String tmp = "("+i+")";
				while (tmp.length()<6)
					tmp+=" ";
				text+= tmp+": ";
				for (int j=0; j<map[0].length; j++) {
					int parent = map[i][j].parent;
					int value = map[i][j].value;
					if (value == BWTree.FILL)
						text+= "....... ";
					else if (value == 0)
						text+= " [root] ";
					else {
						String val = new String();
						String par = new String();
						if (value < 10)
							val+=" ";
						if (parent < 10)
							par+=" ";
						text+= "["+val+value+":"+par+parent+"] ";
					}
				}
				text+='\n';
				text+='\n';
			}
			treeTexter.setText(text);
			otherFrame = new CloseableFrame("Blue Wizard Tree Frame",treeTexter);
			mainSetter.setComponent(treeTexter);
			mainSetter.setComponent(otherFrame);
			otherFrame.addWindowListener(this);
			otherFrame.setSize(xDimension,xDimension/2);
			otherFrame.show();
			setMedia(otherFrame);
		}
	}

	public void actionPerformed(ActionEvent e)
	{
		Object obj = e.getSource();
		switchCursor();
		if (obj == actionButton[3] || obj == actionMI[3])
			handleConfigProblem();
		else if (obj == actionButton[0] || obj == actionMI[0])
			handleSearchSolution();
		else if (obj == actionButton[2] || obj == actionMI[2])
			handleShowSolution();
		else if (obj == actionButton[1] || obj == actionMI[1])
			handleTreeFrame();
		else if (obj == actionButton[4] || obj == actionMI[4])
			handleShowHistory();
		else if (obj == helpMI[0])
			handleHelp();

	}

	public void windowActivated(WindowEvent e) {}

	public void windowClosed(WindowEvent e)
	{
		if (e.getSource() == otherFrame)
			show();
	}

	public void windowClosing(WindowEvent e)
	{
		if (e.getSource() == this)
			dispose();
	}

	public void windowDeactivated(WindowEvent e) {}

	public void windowDeiconified(WindowEvent e)  {}

	public void windowIconified(WindowEvent e) {}

	public void windowOpened(WindowEvent e) {}

	/*** layout component ***/

	// constant
	private final static String[] ACTIONS = { "Search Solution", "Open Tree Frame", "Open Solution Frame", "Open Config Frame", "Open History Frame" };
	private final static int[] ACTIONS_KEY = { KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5 };
	private final static int[] TIMEOUT = { 0, 5, 10, 30, 60, 120, 180 };
	private final static int[] DEPTH = { 0, 5, 10, 20, 30 ,40 ,50, 100 };
	private final static Color backColor = new Color(0,0,40);
	private final static Color foreColor = new Color(230,230,255);
	private final static Color indColor = new Color(200,100,100);

	// main settings
	private BWData awtData;
	private int currentProblemIndex;
	private int xDimension;
	private AWTSetter mainSetter, mainSetterBold;
	private BWSolver solvingAgent;
	private BWProblem currentProblem;
	private BWProblem[] problems;
	private BWLoader problemLoader;
	private Frame otherFrame;
	private SearchNode solutionFound;
	private Vector searchHistory;
	private Properties searchProp;
	private SearchStatsTree stats;

	// menu
	private MenuBar menuBar;
	private Menu actionMenu, searchMenu, problemMenu, timeOutMenu, expansionMenu, depthLimitMenu, helpMenu;
	private CheckboxMenuItem[] searchMI, problemMI, timeOutMI, expansionMI, depthLimitMI;
	private MenuItem[] helpMI;
	private MenuItem[] actionMI;

	//component
	private Label messageLabel;
	private Panel mainPanel, leftPanel, rightPanel;
	// left panel
	private Button[] actionButton;
	private Panel problemDataPanel;
	private Label problemNameLabel;
	private Texter problemDescriptionArea;
	private Label searchTimeOut, searchExpansion, searchDepth, searchType;
	// right panel
	private ImageCanvas logoCanvas;
	private Panel statsPanel;
	private IndicatorLabel[] statsLabel;

}
