package edu.unl.consystlab.sudokuSolver.consistencyAlgorithms;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import edu.unl.consystlab.sudokuSolver.constraintProblem;
import edu.unl.consystlab.sudokuSolver.problemConstraint;
import edu.unl.consystlab.sudokuSolver.problemVariable;
import edu.unl.consystlab.sudokuSolver.sudokuBoard;

public class binaryRestrictedArcConsistency extends consistencyAlgorithm {

	private List arcConsistentVariables;
	
	public binaryRestrictedArcConsistency(constraintProblem newProblem, sudokuBoard board, List newArcConsistentVariables) {
		super(newProblem, board);
		arcConsistentVariables = newArcConsistentVariables;
	}

	public boolean runAlgorithm()
	{
		variableReductions = new LinkedList();
		parentProblem.registerDomainReductionListener(variableReductions);
		
		
		Stack constraintQueue = new Stack();
		//we need to get all the constraints between variables that are in this list.
		//to do this we get every variable's constraints check to see if the other variable
		//participating in the contraint is also in the list then check to see that it is not
		//already in the queue, if not then we add it to the constraintQueue.
		
		for(int variableIndex = 0; variableIndex < arcConsistentVariables.size(); variableIndex++)
		{
			constraintQueue.addAll(findRelevantConstraints(
					(problemVariable)arcConsistentVariables.get(variableIndex), arcConsistentVariables));
		}

		problemConstraint currentConstraint;
		while(constraintQueue.size() > 0)
		{
			currentConstraint = (problemConstraint)constraintQueue.pop();
			if(currentConstraint.isViolated())
			{
				setBrokenConstraint(currentConstraint);
				setEncounteredError(true);
				parentProblem.unregisterDomainReductionList(variableReductions);
				return false;
			}
			if(  currentConstraint.revise( (problemVariable)(currentConstraint.getScope()).get(0) )  )
			{
				//add all the constraints related to the current variable back
				constraintQueue.addAll(findRelevantConstraints(
						(problemVariable)((currentConstraint.getScope()).get(0)), arcConsistentVariables));
			}
			if(  currentConstraint.revise( (problemVariable)(currentConstraint.getScope()).get(1) )  )
			{
				//add all the constraints related to the current variable back
				constraintQueue.addAll(findRelevantConstraints(
						(problemVariable)((currentConstraint.getScope()).get(1)), arcConsistentVariables));
			}
		}
		
		setBrokenConstraint(null);
		setEncounteredError(false);
		parentProblem.unregisterDomainReductionList(variableReductions);
		return true;		


		
	}

	//this takes
	//the current variable we are working with
	//and the list of variables we are concerned with
	//it returns a list of constraints that should be revised.
	private List findRelevantConstraints(problemVariable currentVariable, List ArcConsistentVariables)
	{
		List constraintQueue = new LinkedList();
		
		List variableConstraints = new LinkedList(
				currentVariable.getBinaryConstraints());
		for(int constraintIndex = 0; constraintIndex < variableConstraints.size(); constraintIndex++)
		{
			//figure out which variable in the scope is not the one that we already
			//selected this constraint for
			//basically this just compares the index of the first variable in the scope of the constraint
			//to that of the variable we already have if it is a match we look to see if the other one is 
			//in the list that was passed to the function.  If it is not that variable we already have then
			//we look at it to see if it is already in the list.
			if( (((problemVariable)
						((LinkedList)
							((problemConstraint)
								variableConstraints.get(constraintIndex))
						    .getScope())
						.get(0))
					.getIndex()).equals( 
						currentVariable.getIndex()) )
			{
				if( ArcConsistentVariables.contains(
						((LinkedList)
							((problemConstraint)
									variableConstraints.get(constraintIndex))
							.getScope())
						.get(1)) )
				{
					constraintQueue.add(variableConstraints.get(constraintIndex));
					//System.out.println("test");
				}
			}
			else
			{
				if( ArcConsistentVariables.contains(
						((LinkedList)
							((problemConstraint)
									variableConstraints.get(constraintIndex))
							.getScope())
						.get(0)) )
				{
					constraintQueue.add(variableConstraints.get(constraintIndex));
					//System.out.println("test2");
				}
			}
		}
		return (constraintQueue);
	}
}
