/*
 * @(#)SPPBWProblem.java
 *
 * Classi private definite in questo sorgente:
 * - SquarePuzzleEvalOne
 * - SPPStateDisplay
 * - SPPConfigFrame
 */
package ereinionbw.spp;

import java.awt.Component;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Choice;
import java.awt.Label;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.Font;
import java.awt.Menu;
import java.awt.CheckboxMenuItem;
import java.awt.MenuBar;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Properties;

import ereinion.search.EvalFunction;
import ereinion.search.SearchOp;
import ereinion.search.SearchState;
import ereinion.search.SearchProblem;
import ereinion.awt.CheckboxMIGroup;
import ereinion.awt.AWTSetter;

import ereinionbw.BWProblem;
import ereinionbw.SearchStateDisplay;
import ereinionbw.ProblemNotInformed;

/**
 * Implementazione del problema del rompicapo dell'8 utilizzabile per Blue Wizard.
 *
 * @author  Ereinion
 * @version 1.0, 7/10/2002
 * @since EPR 1.0
 * @see ereinionbw.spp.SquarePuzzleProblem
 */
public class SPPBWProblem implements BWProblem
{

	public SPPBWProblem(AWTSetter setter, int xDim)
	{
		problem = new SquarePuzzleProblem(3);
		displaySet(setter,xDim);
	}

	public String getProblemName() { return "Puzzle of 8"; }

	public String[] getProblemDescription() {
		String[] result = new String[5];
		result[0] = new String("Initial State : Randomizable");
		result[1] = new String("Operators : Box close to the blank one");
		result[2] = new String("Goal : The state where the boxes are ordered");
		result[3] = new String("Path Cost : 1 for each operator");
		result[4] = new String("Author : Ereinion");
		return result;
	}

	public SearchProblem getSearchProblem() { return problem; }

	public EvalFunction getEvalFunction() throws ProblemNotInformed
	{ return (new SquarePuzzleEvalOne(new SquarePuzzleState(SquarePuzzleState.dimension((SquarePuzzleState)problem.initialState())))); }

	public Frame getConfigFrame()
	{ return new SPPConfigFrame(this,awtSetter,xDimension); }

	public SearchStateDisplay getStateDisplayer()
	{ return new SPPStateDisplay(); }

	public void displaySet(AWTSetter setter, int xDim)
	{ awtSetter = setter; xDimension = xDim; }

	public void randomizeInitState(int random) { problem.randomizeInitState(random); }

	
	/* campi privati */

	private SquarePuzzleProblem problem;
	private AWTSetter awtSetter;
	private int xDimension;

}


/*
 * classe privata : SquarePuzzleEvalOne
 *
 * Questa classe implementa una euristica per il problema del rompicapo dell'8 basata sul seguente criterio:<br>
 * La funzione evalState restituisce il numero di caselle che non si trovano nella posizione corretta.
 */
class SquarePuzzleEvalOne implements EvalFunction
{

	private SquarePuzzleState goalState;

	public SquarePuzzleEvalOne(SquarePuzzleState gs) { goalState = gs; }

	public int evalState(SearchState state)
	{
		SquarePuzzleState s = (SquarePuzzleState)state;
		int value = 0;
		int dim = SquarePuzzleState.dimension(s);
		for (int i=0; i<dim; i++)
			for (int j=0; j<dim; j++) {
				int gv = SquarePuzzleState.getBox(goalState,i,j);
				int sv = SquarePuzzleState.getBox(s,i,j);
				if (sv!=SquarePuzzleState.BLANK_VALUE && sv!=gv)
					value++;
			}
		return value;
	}

}


/*
 * classe privata : SPPStateDisplay
 *
 * Finestra di configurazione per SPPBWProblem.
 */
class SPPStateDisplay extends SearchStateDisplay
{

	private SquarePuzzleState currentState;

	public SPPStateDisplay()
	{
		super();
		currentState = null;
	}

	public void displayState(SearchState s)
	{
		currentState = (SquarePuzzleState)s;
		repaint();
	}

	public SquarePuzzleOp getOperator(int mouseX, int mouseY)
	{
		int dim = SquarePuzzleState.dimension(currentState);
		int dX = getSize().width;
		int dY = getSize().height;
		int size = dX;
		if (dX > dY)
			size = dY;
		int squareSize = size/dim;
		int currentX = (dX-dim*squareSize)/2;
		int currentY = (dY-dim*squareSize)/2;
		int opX = (mouseX-currentX)/squareSize;
		int opY = (mouseY-currentY)/squareSize;
		return (new SquarePuzzleOp(opX,opY));
	}

	public void paint(Graphics g)
	{
		if (currentState != null) {
			int dim = SquarePuzzleState.dimension(currentState);
			int dX = getSize().width;
			int dY = getSize().height;
			int size = dX;
			if (dX > dY)
				size = dY;
			int squareSize = size/dim;
			int currentX = (dX-dim*squareSize)/2;
			int currentY = (dY-dim*squareSize)/2;
			if (dim>5) {
				Font f = getFont();
				g.setFont(new Font(f.getName(),f.getStyle(),f.getSize()/3*2));
			} else
				g.setFont(getFont());
			for (int i=0; i<dim; i++) {
				for (int j=0; j<dim; j++) {
					int value = SquarePuzzleState.getBox(currentState,j,i);
					g.setColor(getForeground());
					if (value != SquarePuzzleState.BLANK_VALUE)
						g.fillRect(currentX+2,currentY+2,squareSize-4,squareSize-4);
					String s = ""+value;
					if (value==SquarePuzzleState.BLANK_VALUE)
						s="";
					g.setColor(getBackground());
					g.drawString(s,currentX+squareSize/2,currentY+squareSize/2);
					currentX+=squareSize;
				}
				currentY+=squareSize;
				currentX=(dX-dim*squareSize)/2;
			}
		}
	}

}


/*
 * classe privata : SPPConfigFrame
 *
 * Finestra di configurazione per SPPBWProblem.
 */
class SPPConfigFrame extends Frame implements WindowListener, ActionListener, MouseListener, ItemListener
{

	private SPPStateDisplay stateDisplayer;

	private final static int[] RANDOM_MENU = { 0, 10, 20, 30 ,40 ,50 ,60 ,70 ,80 ,90, 100 };

	private CheckboxMenuItem[] randomMI, dimMI;

	private EvalFunction evalFun;
	private Choice dimChoice, randomChoice;
	private Button resetButton, randomButton;
	private Label evalLabel, dimLabel, randomLabel, messageLabel;
	private AWTSetter mainSetter;

	private SPPBWProblem problem;

	private int stateDim, stateRan;

	public SPPConfigFrame(SPPBWProblem sp, AWTSetter setter, int xDim)
	{
		// base settings
		super("Blue Wizard Config Frame for : "+sp.getProblemName());

		problem = sp;

		mainSetter = setter;
		mainSetter.setComponent(this);

		stateDim = SquarePuzzleState.dimension((SquarePuzzleState)problem.getSearchProblem().initialState());
		stateRan = 0;

		// menu layout
		MenuBar frameBar = new MenuBar();
		Menu randomMenu, dimMenu;
		randomMenu = (Menu)mainSetter.setMenuItem(new Menu("Random Factor"));
		CheckboxMIGroup randomGroup = new CheckboxMIGroup();
		randomMI = new CheckboxMenuItem[RANDOM_MENU.length];
		for (int k=0; k<randomMI.length; k++) {
			randomMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(randomGroup.makeBox(""+RANDOM_MENU[k],false),randomMenu);
			randomMI[k].addItemListener(this);
		}
		randomMI[0].setState(true);
		frameBar.add(randomMenu);
		dimMenu = (Menu)mainSetter.setMenuItem(new Menu("Dimension"));
		int dimRange = SquarePuzzleState.MAX_DIMENSION-SquarePuzzleState.MIN_DIMENSION;
		dimMI = new CheckboxMenuItem[dimRange];
		CheckboxMIGroup dimGroup = new CheckboxMIGroup();
		for (int k=0; k<dimRange; k++) {
			dimMI[k] = (CheckboxMenuItem)mainSetter.setMenuItem(dimGroup.makeBox(""+(SquarePuzzleState.MIN_DIMENSION+k),false),dimMenu);
			dimMI[k].addItemListener(this);
		}
		dimMI[stateDim].setState(true);
		frameBar.add(dimMenu);
		setMenuBar(frameBar);

		// layout
		setLayout(new GridLayout(1,1));
		Panel mainPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(1,2)));
		Panel leftPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(2,1)));
		Panel controlPanel = (Panel)mainSetter.setComponent(new Panel(new GridLayout(5,1)));
		resetButton = (Button)mainSetter.setComponent(new Button("reset"),controlPanel);
		resetButton.addActionListener(this);
		Panel labelPanel1 = (Panel)mainSetter.setComponent(new Panel(new GridLayout(1,2)));
		mainSetter.setComponent(new Label("Dimension:",Label.RIGHT),labelPanel1);
		dimLabel = (Label)mainSetter.setComponent(new Label("3",Label.LEFT),labelPanel1);
		Panel labelPanel2 = (Panel)mainSetter.setComponent(new Panel(new GridLayout(1,2)));
		mainSetter.setComponent(new Label("Eval State:",Label.RIGHT),labelPanel2);
		evalLabel = (Label)mainSetter.setComponent(new Label("0",Label.LEFT),labelPanel2);
		Panel labelPanel3 = (Panel)mainSetter.setComponent(new Panel(new GridLayout(1,2)));
		mainSetter.setComponent(new Label("Randomization:",Label.RIGHT),labelPanel3);
		randomLabel = (Label)mainSetter.setComponent(new Label("0",Label.LEFT),labelPanel3);
		controlPanel.add(labelPanel1);
		controlPanel.add(labelPanel2);
		controlPanel.add(labelPanel3);
		messageLabel = (Label)mainSetter.setComponent(new Label("Search Mode",Label.CENTER),controlPanel);
		leftPanel.add(controlPanel);
		mainSetter.setComponent(new Label(),leftPanel);
		mainPanel.add(leftPanel);
		stateDisplayer = (SPPStateDisplay)mainSetter.setComponent(new SPPStateDisplay(),mainPanel);
		stateDisplayer.displayState(problem.getSearchProblem().initialState());
		stateDisplayer.addMouseListener(this);
		add(mainPanel);

		// last settings
		setSize(xDim,xDim/2);
		setResizable(false);

		handleReset();

		addWindowListener(this);
		show();
	}

	private void handleReset()
	{
		SquarePuzzleProblem spp = (SquarePuzzleProblem)problem.getSearchProblem();
		spp.reset(stateDim);
		spp.randomizeInitState(stateRan);
		stateDisplayer.displayState(spp.initialState());
		dimLabel.setText(""+stateDim);
		try {
			evalLabel.setText(""+problem.getEvalFunction().evalState(problem.getSearchProblem().initialState()));
		} catch (ProblemNotInformed e) {}
	}

	public void itemStateChanged(ItemEvent e)
	{
		for (int k=0; k<randomMI.length; k++)
			if (e.getSource() == randomMI[k]) {
				stateRan = RANDOM_MENU[k];
				randomLabel.setText(""+RANDOM_MENU[k]);
			}
		for (int k=0; k<dimMI.length; k++)
			if (e.getSource() == dimMI[k]) {
				stateDim = SquarePuzzleState.MIN_DIMENSION+k;
				dimLabel.setText(""+stateDim);
				handleReset();
			}
	}

	public void actionPerformed(ActionEvent e)
	{
		if (e.getSource() == resetButton)
			handleReset();
	}

	public void mouseClicked(MouseEvent e)
	{
		SquarePuzzleOp op = stateDisplayer.getOperator(e.getX(),e.getY());
		messageLabel.setText("x:"+e.getX()+" y:"+e.getY()+" "+op.operatorToString());
		SquarePuzzleProblem spp = (SquarePuzzleProblem)problem.getSearchProblem();
		SearchOp[] opList = spp.operatorList(spp.initialState());
		boolean found = false;
		for (int k=0; k<opList.length && !found; k++) {
			SquarePuzzleOp current = (SquarePuzzleOp)opList[k];
			if (SquarePuzzleOp.getX(current)==SquarePuzzleOp.getX(op) && SquarePuzzleOp.getY(current)==SquarePuzzleOp.getY(op)) {
				spp.setInitState((SquarePuzzleState)spp.applyOperator(spp.initialState(),op));
				found = true;
				stateDisplayer.displayState(spp.initialState());
			}
		}
	}

	public void mouseEntered(MouseEvent e) { }

	public void mouseExited(MouseEvent e) { }

	public void mousePressed(MouseEvent e) { }

	public void mouseReleased(MouseEvent e) { }

	public void windowActivated(WindowEvent e)  {}

	public void windowClosed(WindowEvent e) {}

	public void windowClosing(WindowEvent e) { dispose(); }

	public void windowDeactivated(WindowEvent e) {}

	public void windowDeiconified(WindowEvent e)  {}

	public void windowIconified(WindowEvent e) {}

	public void windowOpened(WindowEvent e) {}

}
