package edu.unl.consystlab.sudokuSolver;

import java.awt.*;
import java.util.List;
import java.util.*;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.arcConsistency;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.backCheckAll;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.backCheckOnVariable;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.binaryForwardCheckAll;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.binaryForwardCheckOnVariable;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.binaryRestrictedArcConsistency;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.consistencyAlgorithm;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.nonBinaryMAC;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.nonBinaryRestrictedArcConsistency;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.shavingGAC;
import edu.unl.consystlab.sudokuSolver.consistencyAlgorithms.shavingMAC;


public class sudokuBoard extends Container {

	private Image offscreen;
	
	constraintProblem parentProblem;
	sudokuInterface parentInterface;
	
	private final int UNIT_BORDER_WIDTH = 3;
	private int test = 0;
	private int boardWidth;
	private int boardHeight;
	private int unitsWide;
	private int unitsHigh;
	private int cellWidth;
	private int cellHeight;
	private int numCols;
	private int numLines;
	private boolean isError;
	private String boardErrors;
	
	//private Hashtable[] lineValueFrequency;
	//private Hashtable[] colValueFrequency;
	//private Hashtable[][] unitValueFrequency;
	
	private Stack undoStack;
	private Stack redoStack;
	
	private sudokuCell[] problemCells;
	
//	//its a list because a variable may have more than one broken constraint
//	//this list is kept so that we have a quick reference of how many times a variable
//	//is in error.
//	private List variablesInError;
	private List constraintsInError;
	
	//holds the sudokuCells hashed on the variable.
	private Hashtable myCells;
	
	private boolean displayingHint;
	private String mouseAction;
        private int num_errors = 0;

	//constructor pass the variable corresponding to this cell here
	public sudokuBoard(constraintProblem myProblem, sudokuInterface myInterface)
	{

		displayingHint = false;
		constraintsInError = new LinkedList();
		
		undoStack = new Stack();
		redoStack = new Stack();
		
		isError = false;
		boardErrors = "";
		parentProblem = myProblem;
		parentInterface = myInterface;

		//mycells holds the cells hashed on the variable.
		myCells = new Hashtable();
		
		if(parentProblem == null)
		{
			isError = true;
			boardErrors = boardErrors + "The problem was not loaded correctly.\n";
		}
		numCols = parentProblem.totalColumns;  //these two should always be equal
		numLines = parentProblem.totalLines;
		
		if(numCols != numLines)
		{
			isError = true;
			boardErrors = boardErrors + "The number of columns and lines should be equal.\n";
		}
		if(numCols > 16)
		{
			isError = true;
			boardErrors = boardErrors + "The number of columns and lines should be less than 16.\n";
		}
		
		if(!isError)
		{
			unitsWide = (int)(numCols/parentProblem.columnsPerUnit);
			unitsHigh = (int)(numLines/parentProblem.linesPerUnit);
	
			//probably need an algorithm to assign these dynamically.
			boardWidth = 360;
			boardHeight = 360;
			
			//add room to draw the lines between the units.
			boardHeight += UNIT_BORDER_WIDTH * (unitsHigh - 1);
			boardWidth += UNIT_BORDER_WIDTH * (unitsWide -1);
			
			this.setSize(boardWidth,boardHeight);
			
			//TODO: need an algorithm to assign these dynamically.
			cellWidth = cellHeight = 40;
			
			problemCells = new sudokuCell[81];
			int tempCellNum =0;
			for(int lineIndex=0; lineIndex <9; lineIndex++)
			{
				for(int colIndex=0; colIndex<9; colIndex++)
				{
                                        
					problemVariable tempVar = parentProblem.getVariable((lineIndex+1)+","+(colIndex+1));
                                        
                                        if((colIndex >=3 && colIndex < 6) && ((lineIndex < 3)||(lineIndex>=6))){
                                            problemCells[tempCellNum] = new sudokuCell(tempVar, cellWidth, cellHeight, this, new Color(200,200,255));
                                        }
                                        else if(((colIndex < 3) || (colIndex >= 6)) && (lineIndex >=3 && lineIndex < 6)){
                                            problemCells[tempCellNum] = new sudokuCell(tempVar, cellWidth, cellHeight, this, new Color(200,200,255));
                                        }
                                        else{
                                            problemCells[tempCellNum] = new sudokuCell(tempVar, cellWidth, cellHeight, this, new Color(255,255,200));
                                        }

					problemCells[tempCellNum].setLocation( ((colIndex*cellHeight)+(int)Math.floor(colIndex/unitsWide)*UNIT_BORDER_WIDTH)
							,((lineIndex*cellWidth)+(int)Math.floor(lineIndex/unitsHigh)*UNIT_BORDER_WIDTH) );
                                        //problemCells[tempCellNum].setSize(10,10);
					problemCells[tempCellNum].setSize(cellWidth,cellHeight);
                                        problemCells[tempCellNum].setFocusable(true);
                                        //problemCells[tempCellNum].isFocusTraversable();
					//problemCells[tempCellNum].addActionListener(this);
                                        
					this.add(problemCells[tempCellNum]);
					myCells.put(tempVar, problemCells[tempCellNum]);
					//System.out.println(tempCellNum);
					tempCellNum++;
				}
			}
		}
                setFocusable(true);
                requestFocusInWindow();
	}
	
	public int getCellWidth()
	{
		return cellWidth;
	}
	public int getCellHeight()
	{
		return cellHeight;
	}
	public int getUnitBorderWidth()
	{
		return UNIT_BORDER_WIDTH;
	}

       	public String getMouseAction() {
		return mouseAction;
	}

        public void setMouseAction(String action) {
		mouseAction = action;
	}
         public void incrementErrors(){
                 num_errors = num_errors + 1;
        }

        public void resetErrors(){
            num_errors = 0;
        }

        public int getNumErrors(){
            return num_errors;
        }

	public void update(Graphics g)
	{
		Graphics bufferGraphics;

		offscreen = createImage(this.getWidth(), this.getHeight());
		bufferGraphics = offscreen.getGraphics();
//		bufferGraphics.setColor(getBackground());
//		bufferGraphics.fillRect(0,0,this.getWidth(), this.getHeight());
//		bufferGraphics.setColor(getForeground());
		for(int i=0; i <= 80; i++)
		{
			bufferGraphics.translate(problemCells[i].getX(), problemCells[i].getY());
			problemCells[i].paint(bufferGraphics);	
			bufferGraphics.translate(-problemCells[i].getX(), -problemCells[i].getY());
		}
		
		
		//bufferGraphics.setClip(0,0,this.getWidth(), this.getHeight());

		//super.paint(bufferGraphics);
		
		if(!isError)
		{
			Graphics2D g2 = (Graphics2D)bufferGraphics;
//			Graphics2D g2 = (Graphics2D)g;
			
			//make the line thicker
			//g2.setStroke(new BasicStroke(3.3f));
			g2.setColor(Color.BLACK);
			
			//draw the unit separating "lines" which are actually
			//filled rectangles.
			
			//do the horizontal lines first
			for(int lineIndex = 1; lineIndex < unitsWide; lineIndex++)
			{
				g2.fillRect(  0, ( lineIndex*(cellHeight*(int)(numLines)/(unitsHigh)) + ((lineIndex-1)*UNIT_BORDER_WIDTH) )
						, boardWidth, UNIT_BORDER_WIDTH  );
			}
			
			//now the veritical lines
			for(int colIndex = 1; colIndex < unitsHigh; colIndex++)
			{
				g2.fillRect( ( colIndex*(cellWidth*(int)(numCols)/(unitsWide)) + ((colIndex-1)*UNIT_BORDER_WIDTH) ), 0
						, UNIT_BORDER_WIDTH, boardHeight  );				
			}			
		}
		else
		{
			Label errorLabel = new Label(boardErrors);
			errorLabel.setLocation(0, 0);
			errorLabel.setSize(this.boardWidth, this.boardHeight);
			
			this.add(errorLabel);
		}
		
		g.drawImage(offscreen,0,0,this);
		offscreen.flush();
		bufferGraphics.dispose();

              
	}
	
	public void paint(Graphics g){

		this.update(g);
                
	}

//	public void runDomainColoring()
//	{
//		//set up the hashtables
//		lineValueFrequency = new Hashtable[parentProblem.totalLines+1];
//		for(int i= 1; i <= parentProblem.totalLines; i++)
//		{
//			//lineValueFrequency[i].clear();
//			lineValueFrequency[i]= new Hashtable();
//		}
//		
//		colValueFrequency = new Hashtable[parentProblem.totalColumns+1];
//		for(int i= 1; i <= parentProblem.totalColumns; i++)
//		{
//			colValueFrequency[i]= new Hashtable();
//		}
//		
//		unitValueFrequency = new Hashtable[parentProblem.columnsPerUnit+1][parentProblem.linesPerUnit+1];
//		for(int i= 1; i <= parentProblem.columnsPerUnit; i++)
//		{
//			for(int j = 1; j <= parentProblem.linesPerUnit; j++)
//			{
//				unitValueFrequency[i][j]= new Hashtable();				
//			}
//		}
//		
//		for(int colIndex= 1; colIndex <= parentProblem.totalColumns; colIndex++)
//		{
//			for(int lineIndex = 1; lineIndex <= parentProblem.totalLines; lineIndex++)
//			{
//				LinkedList valueList = new LinkedList(parentProblem.getVariable(colIndex + "," + lineIndex).getEntireDomain());
//				while(!valueList.isEmpty())
//				{
//					String currentValue = (String)valueList.get(0);
//					valueList.remove(0);
//					
//					//increment the number of times that value appears in the line
//					if(!lineValueFrequency[lineIndex].containsKey(currentValue))
//					{
//						lineValueFrequency[lineIndex].put(
//								currentValue,
//								new Integer( 1 ) );
//					}
//					else
//					{
//						lineValueFrequency[lineIndex].put(
//								currentValue,
//								new Integer( ((Integer)lineValueFrequency[lineIndex].get(currentValue)).intValue() + 1 ) );
//					}
//					
//					//increment the number of times that value appears in the column
//					if(!colValueFrequency[colIndex].containsKey(currentValue))
//					{
//						colValueFrequency[colIndex].put(
//								currentValue,
//								new Integer( 1 ) );
//					}
//					else
//					{
//						colValueFrequency[colIndex].put(
//								currentValue,
//								new Integer( ((Integer)colValueFrequency[colIndex].get(currentValue)).intValue() + 1 ) );
//					}
//					
//					//increment the number of times that value appears in an unit
//					if(!unitValueFrequency
//							[(int)(Math.floor((lineIndex-1)/parentProblem.linesPerUnit)) +1]
//							 [(int)(Math.floor((colIndex-1)/parentProblem.columnsPerUnit)) +1]
//							  .containsKey(currentValue))
//					{
//						unitValueFrequency
//						[(int)(Math.floor((lineIndex-1)/parentProblem.linesPerUnit)) +1]
//						 [(int)(Math.floor((colIndex-1)/parentProblem.columnsPerUnit)) +1]
//						 .put(
//								currentValue,
//								new Integer( 1 ) );
//					}
//					else
//					{
//						unitValueFrequency
//						[(int)(Math.floor((lineIndex-1)/parentProblem.linesPerUnit)) +1]
//						 [(int)(Math.floor((colIndex-1)/parentProblem.columnsPerUnit)) +1]
//						  .put(
//								currentValue,
//								new Integer( ((Integer)unitValueFrequency
//										[(int)(Math.floor((lineIndex-1)/parentProblem.linesPerUnit)) +1]
//										 [(int)(Math.floor((colIndex-1)/parentProblem.columnsPerUnit)) +1]
//										  .get(currentValue)).intValue() + 1 ) );
//					}
//				}
//			}
//		}
//		return;
//	}
	
	public Color getDomainValueColor(int xIndex, int yIndex, String value)
	{
//		//if it is the only variable in the unit, row, or column set its color green
//		if(  (unitValueFrequency
//				[(int)(Math.floor((xIndex-1)/parentProblem.linesPerUnit)) +1]
//				 [(int)(Math.floor((yIndex-1)/parentProblem.columnsPerUnit)) +1].containsKey(value) &&
//				 ((Integer)(unitValueFrequency
//					[(int)(Math.floor((xIndex-1)/parentProblem.linesPerUnit)) +1]
//					 [(int)(Math.floor((yIndex-1)/parentProblem.columnsPerUnit)) +1].get(value))).intValue() == 1)
//					||  (colValueFrequency[yIndex].containsKey(value) &&
//							((Integer)(colValueFrequency[yIndex].get(value))).intValue() == 1)
//							||  (lineValueFrequency[xIndex].containsKey(value) &&
//									((Integer)(lineValueFrequency[xIndex].get(value))).intValue() == 1)  )
//		{
//			//looks green
//			Fcolorreturn(new Color(10,200,20));
//		}
//		else if( (unitValueFrequency
//				[(int)(Math.floor((xIndex-1)/parentProblem.linesPerUnit)) +1]
//				 [(int)(Math.floor((yIndex-1)/parentProblem.columnsPerUnit)) +1].containsKey(value) &&
//				 ((Integer)(unitValueFrequency
//					[(int)(Math.floor((xIndex-1)/parentProblem.linesPerUnit)) +1]
//					 [(int)(Math.floor((yIndex-1)/parentProblem.columnsPerUnit)) +1].get(value))).intValue() == 2)
//					||  (colValueFrequency[yIndex].containsKey(value) &&
//							((Integer)(colValueFrequency[yIndex].get(value))).intValue() == 2)
//							||  (lineValueFrequency[xIndex].containsKey(value) &&
//									((Integer)(lineValueFrequency[xIndex].get(value))).intValue() == 2) )
//		{
//			//looks dark yellow
//			return(new Color(200,200,29));
//		}
		return(Color.BLACK);
	}
	
	public void depress(List variablesAffected)
	{
		Iterator i = variablesAffected.iterator();
		while(i.hasNext())
		{
			((sudokuCell)myCells.get(i.next())).depress();
		}
		return;
	}
	
	public void undepress(List variablesAffected)
	{
		Iterator i = variablesAffected.iterator();
		while(i.hasNext())
		{
			((sudokuCell)myCells.get(i.next())).undepress();
		}
		return;
	}
	
	public void doAutoPropogation(problemVariable originalVariable)
	{
		parentInterface.doAutoPropogation(originalVariable);
	}
	
	public void makeAssignment(problemVariable variableToAssign, String valueToAssign)
	{
                resetErrors();
                System.out.println("reset errors " + Integer.toString(num_errors));
		List myDomainReductions = new LinkedList();
		parentProblem.registerDomainReductionListener(myDomainReductions);
		variableToAssign.setAssigned(valueToAssign);
		parentProblem.unregisterDomainReductionList(myDomainReductions);

		add_assignment_undo(variableToAssign, myDomainReductions);
		this.repaint();
  
	}

        public void check_if_complete(){
           
                boolean res = check_if_solved(parentProblem);
              
               if(parentInterface.getHaveUsed()==0){
                if(res==false){

                  try {
                        parentInterface.stopTimer();
                        show_high_scores_box();

                    } catch (Exception ex) {
                        Logger.getLogger(sudokuInterface.class.getName()).log(Level.SEVERE, null, ex);
                    }
               }

           }

        }
       private void show_high_scores_box() throws Exception{

                 dbmysql database = new dbmysql();
                 database.is_high_score(parentInterface.getHours(),parentInterface.getMinutes(), parentInterface.getSeconds());




        }

        private boolean check_if_solved(constraintProblem newProblem){
              test++;
            boolean not_done = false;
            parentProblem = newProblem;
            List allVariables = new LinkedList(parentProblem.getAllVariables());

            Iterator i = allVariables.iterator();

		while(i.hasNext())
		{

                    problemVariable currentVar = (problemVariable)i.next();
                    if(!currentVar.isAssigned())
			{
                        not_done = true;
                    }
                }
            if(num_errors>0){
                not_done = true;
            }
            return not_done;
        }
	
	private void add_assignSingletons_undo(assignSingletons newAssignSingletons)
	{
		user_action temp_user_action = new user_action();
		temp_user_action.user_assignSingletons(newAssignSingletons);
		undoStack.push(temp_user_action);
		redoStack.clear();
		actionOccured();
	}
	
	private void add_assignment_undo(problemVariable new_variableAffected, List domainReductions)
	{
		user_action temp_user_action = new user_action();
		temp_user_action.user_assign(new_variableAffected, domainReductions);
		undoStack.push(temp_user_action);
		redoStack.clear();
		actionOccured();
	}
	
	public void add_consistency_undo(consistencyAlgorithm newAlgorithm)
	{
		parentInterface.updateValuesRemoved(newAlgorithm.getVariableReductions().size());
		
		user_action temp_user_action = new user_action();
		temp_user_action.user_consistency(newAlgorithm);
		if(newAlgorithm.isEncounteredError())
		{
			addNewBrokenConstraint(newAlgorithm.getBrokenConstraint());
		}
		undoStack.push(temp_user_action);
		redoStack.clear();
		actionOccured();
	}
	
	public void add_reduction_undo(List domainReductions)
	{
		//parentInterface.updateValuesRemoved(newAlgorithm.getVariableReductions().size());
		
		user_action temp_user_action = new user_action();
		temp_user_action.user_domain_reduction(domainReductions);
		undoStack.push(temp_user_action);
		redoStack.clear();
	}
	
	public void preform_undo()
	{
		actionOccured();
		//clear the label that shows how many were updated
		parentInterface.updateValuesRemoved("");
		
		//make sure there is something to undo, although this should be a mute point
		//if we eventually grey out the button.
		if(undoStack.isEmpty())
		{
			return;
		}
		user_action actionToUndo = (user_action)undoStack.pop();
		redoStack.push(actionToUndo);
		
		
		if(actionToUndo.getAction().equals("assign"))
		{
			actionToUndo.setStringAssigned(actionToUndo.getVariableAffected().getAssigned());
			actionToUndo.getVariableAffected().unSetAssigned();
			Iterator i = (actionToUndo.getDomainReductions()).iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping variableValue = (variableIndexAndValueGrouping)i.next();
				parentProblem.getVariable(variableValue.getVariableIndex())
					.addToCurrentDomain(variableValue.getValue());
			}
		}

		else if(actionToUndo.getAction().equals("consistency"))
		{
			Iterator i = actionToUndo.getConsistencyAlgorithm().getVariableReductions().iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping currentReduction = (variableIndexAndValueGrouping)i.next();
				parentProblem.getVariable(currentReduction.getVariableIndex())
					.addToCurrentDomain(currentReduction.getValue());
			}
			
			if(actionToUndo.getConsistencyAlgorithm().isEncounteredError())
			{
				removeConstraintInError(actionToUndo.getConsistencyAlgorithm().getBrokenConstraint());
			}
		}
		else if(actionToUndo.getAction().equals("autoAssign"))
		{
			//unassign everything
			Iterator i = actionToUndo.getAssignments().iterator();
			while(i.hasNext())
			{
				parentProblem.getVariable(((variableIndexAndValueGrouping)i.next()).getVariableIndex()).unSetAssigned();
			}
			
			//add back the domains
			Iterator j = actionToUndo.getDomainReductions().iterator();
			while(j.hasNext())
			{
				variableIndexAndValueGrouping domainReduction = (variableIndexAndValueGrouping)j.next();
				parentProblem.getVariable(domainReduction.getVariableIndex())
					.addToCurrentDomain(domainReduction.getValue());
			}
		}
		else if(actionToUndo.getAction().equals("reduction"))
		{
			Iterator i = actionToUndo.getDomainReductions().iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping currentReduction = (variableIndexAndValueGrouping)i.next();
				parentProblem.getVariable(currentReduction.getVariableIndex())
					.addToCurrentDomain(currentReduction.getValue());
			}
		}

		this.repaint();
	}

	
	public void preform_redo()
	{
		actionOccured();
		//make sure there is something to redo, although this should be a mute point
		//if we eventually grey out the button.
		if(redoStack.isEmpty())
		{
			return;
		}
		user_action actionToRedo = (user_action)redoStack.pop();
		undoStack.push(actionToRedo);
		
		
		if(actionToRedo.getAction().equals("assign"))
		{
			actionToRedo.getVariableAffected().setAssigned(actionToRedo.getStringAssigned());
//			Iterator i = (actionToRedo.getDomainReductions()).iterator();
//			while(i.hasNext())
//			{
//				variableIndexAndValueGrouping variableValue = (variableIndexAndValueGrouping)i.next();
//				parentProblem.getVariable(variableValue.getVariableIndex())
//					.addToCurrentDomain(variableValue.getValue());
//			}
		}

		else if(actionToRedo.getAction().equals("consistency"))
		{
			Iterator i = actionToRedo.getConsistencyAlgorithm().getVariableReductions().iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping currentReduction = (variableIndexAndValueGrouping)i.next();

				parentProblem.getVariable(currentReduction.getVariableIndex())
					.removeFromCurrentDomain(currentReduction.getValue());
			}
			
			if(actionToRedo.getConsistencyAlgorithm().isEncounteredError())
			{
				this.addNewBrokenConstraint(actionToRedo.getConsistencyAlgorithm().getBrokenConstraint());
			}

		}
		else if(actionToRedo.getAction().equals("autoAssign"))
		{
			//reassign everything
			//because assigning singletons consists of taking one value from the domain
			// of a variable and assigning that variable that value we can use the domain reductions
			// to reassign everything.
			Iterator i = actionToRedo.getAssignments().iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping myGrouping = (variableIndexAndValueGrouping)i.next();
				parentProblem.getVariable(myGrouping.getVariableIndex())
					.setAssigned(myGrouping.getValue());
			}
		}
		else if(actionToRedo.getAction().equals("reduction"))
		{
			Iterator i = actionToRedo.getDomainReductions().iterator();
			while(i.hasNext())
			{
				variableIndexAndValueGrouping currentReduction = (variableIndexAndValueGrouping)i.next();

				parentProblem.getVariable(currentReduction.getVariableIndex())
					.removeFromCurrentDomain(currentReduction.getValue());
			}
		}

		//this.doAutoPropogation();
		//this.runDomainColoring();
		this.repaint();
	}
	
//	public void nonBinaryRestrictedArcConsistency(String location)
//	{
//		if(((nonBinaryIntensiveConstraint)
//				(parentProblem.getNonBinaryIntensiveConstraint(location))).updateGraph() == null)
//		{
//			//if it is null then there is an error in the constraint
//			addNewBrokenConstraint(parentProblem.getNonBinaryIntensiveConstraint(location));
//		}
//		add_consistency_undo();
//		this.runDomainColoring();
//		this.repaint();
//	}
	
	public boolean showDomains()
	{
		if(parentInterface.showDomains())
		{
			return(true);
		}
		else
		{
			return(false);
		}
	}

	public void preformReduction(problemVariable variableToReduce, String reduction)
	{

		if(!variableToReduce.isAssigned())
		{
			List myReductions = new LinkedList();
			parentProblem.registerDomainReductionListener(myReductions);
			variableToReduce.removeFromCurrentDomain(reduction);
			parentProblem.unregisterDomainReductionList(myReductions);
			this.add_reduction_undo(myReductions);
			this.repaint();
		}

		
	}
	public Thread preformBackChecking()
	{
		return new backCheckAll(parentProblem, this);
	}
	public void preformBackCheckingOnVariable(problemVariable newMasterVariable)
	{
		consistencyAlgorithm myAlgorithm = new backCheckOnVariable(parentProblem, this, newMasterVariable);
        try {
            myAlgorithm.runAlgorithm();
        } catch (InterruptedException ex) {
            Logger.getLogger(sudokuBoard.class.getName()).log(Level.SEVERE, null, ex);
        }
		this.add_consistency_undo(myAlgorithm);
		this.repaint();
	}
	public Thread preformArcConsistency()
	{
		return new arcConsistency(parentProblem, this);
	}
	public void preformBinaryForwardCheckOnVariable(problemVariable newMasterVariable)
	{
		consistencyAlgorithm myAlgorithm = new binaryForwardCheckOnVariable(parentProblem, this, newMasterVariable);
        try {
            myAlgorithm.runAlgorithm();
        } catch (InterruptedException ex) {
            Logger.getLogger(sudokuBoard.class.getName()).log(Level.SEVERE, null, ex);
        }
		this.add_consistency_undo(myAlgorithm);
		this.repaint();
	}
	public void preformBinaryForwardCheckAll()
	{
		consistencyAlgorithm myAlgorithm = new binaryForwardCheckAll(parentProblem, this);
        try {
            myAlgorithm.runAlgorithm();
        } catch (InterruptedException ex) {
            Logger.getLogger(sudokuBoard.class.getName()).log(Level.SEVERE, null, ex);
        }
		this.add_consistency_undo(myAlgorithm);
		this.repaint();
	}
	public void preformBinaryRestrictedArcConsistency(List newVariables)
	{
		consistencyAlgorithm myAlgorithm = new binaryRestrictedArcConsistency(parentProblem, this, newVariables);
        try {
            myAlgorithm.runAlgorithm();
        } catch (InterruptedException ex) {
            Logger.getLogger(sudokuBoard.class.getName()).log(Level.SEVERE, null, ex);
        }
		this.add_consistency_undo(myAlgorithm);
		this.repaint();
	}
	public Thread preformNonBinaryMAC()
	{
		return new nonBinaryMAC(parentProblem, this);
	}
	public void preformNonBinaryRestrictedArcConsistency(String newLocation)
	{
		consistencyAlgorithm myAlgorithm = new nonBinaryRestrictedArcConsistency(parentProblem, this, newLocation);
        try {
            myAlgorithm.runAlgorithm();
        } catch (InterruptedException ex) {
            Logger.getLogger(sudokuBoard.class.getName()).log(Level.SEVERE, null, ex);
        }
		this.add_consistency_undo(myAlgorithm);
		this.repaint();
	}
	public Thread preformShavingGAC()
	{
		return new shavingGAC(parentProblem, this);
	}
	public Thread preformShavingMAC()
	{
		return new shavingMAC(parentProblem, this);
	}
	public void preformAutoAssign()
	{
		assignSingletons myAlgorithm = new assignSingletons(parentProblem);
		myAlgorithm.runAlgorithm();
		this.add_assignSingletons_undo(myAlgorithm);
		parentInterface.doAutoPropogation();
		this.repaint();
	}
	
	public void addNewBrokenConstraint(problemConstraint newConstraint)
	{
		constraintsInError.add(newConstraint);
		Iterator i = newConstraint.getScope().iterator();
		while(i.hasNext())
		{
			((sudokuCell)myCells.get(i.next())).addError();
		}
		
		parentInterface.setEnableHints(false);
		return;
		
	}

	public void removeConstraintInError(problemConstraint oldConstraint)
	{
		constraintsInError.remove(oldConstraint);
		Iterator i = oldConstraint.getScope().iterator();
		while(i.hasNext())
		{
			((sudokuCell)myCells.get(i.next())).reduceError();
		}
		
		if(constraintsInError.size() == 0)
		{
			parentInterface.setEnableHints(true);
		}
		return;
	}

	public void displayHint(hints myHint)
	{
		displayingHint = true;
		if(!myHint.foundError())
		{
			if(myHint.hasHint())
			{
				sudokuCell myCell = (sudokuCell)this.myCells.get(myHint.getCurrentHintVariable());
				myCell.setHint(true);
			}
			else
			{
				//it hasn't found a hint or an error
				//don't do anything for now.
			}
		}
		else
		{
			//highlight the error
			List errorVariables = new LinkedList(myHint.getErrorConstraint().getScope());
			Iterator i = errorVariables.iterator();
			while(i.hasNext())
			{
				problemVariable currentVariable = (problemVariable)i.next();
				((sudokuCell)myCells.get(currentVariable)).addError();
				
			}
		}
		this.repaint();
	}
	
	public void hideHint(hints myHint)
	{

		if(!myHint.foundError())
		{
			if(myHint.hasHint())
			{
				sudokuCell myCell = (sudokuCell)this.myCells.get(myHint.getCurrentHintVariable());
				myCell.setHint(false);
			}
			else
			{
				//it hasn't found a hint or an error
				//don't do anything for now.
			}
		}
		else
		{
			//unhighlight the error
			List errorVariables = new LinkedList(myHint.getErrorConstraint().getScope());
			Iterator i = errorVariables.iterator();
			while(i.hasNext())
			{
				problemVariable currentVariable = (problemVariable)i.next();
				((sudokuCell)myCells.get(currentVariable)).reduceError();
				
			}
		}
		displayingHint = false;
		this.repaint();
	}

	private void actionOccured() {
		if(constraintsInError.size() == 0  && !displayingHint)
		{
			parentInterface.resetHintBoard();
		}
	}
}
