package edu.unl.consystlab.sudokuSolver;
//References:
//http://www.idevelopment.info/data/Programming/java/xml/SAXExample.java
//http://en.wikipedia.org/wiki/Document_Type_Definition

import java.io.*;
import java.util.List;
import java.util.LinkedList;

import javax.xml.parsers.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class parserXML {

	private constraintProblem describedProblem;

	//default constructor
	public parserXML()
	{


	}

	public void parseXML(String fileName)
	{
			describedProblem = new constraintProblem();
//Integer myint = new Integer(2);
//variableDomains.put(myint, describedProblem);

		DocumentBuilderFactory factoryBuilder;
		DocumentBuilder builder;
		Document doc;
		Element docElement;
		NodeList currentNodeList;


		try
		{
			factoryBuilder  = DocumentBuilderFactory.newInstance( );
			builder = factoryBuilder.newDocumentBuilder();
			doc = builder.parse( fileName );
			docElement = doc.getDocumentElement();

			//Read the presentation properties like name
			currentNodeList = docElement.getElementsByTagName("presentation");
			interpretPresentation(currentNodeList);

			//Get the dimensions
			currentNodeList = docElement.getElementsByTagName("dimensions");
			interpretDimensions(currentNodeList);

			//Generate the domain
			// it will be a list equaling 1..(unitLines*unitColumns)
			generateDomain(describedProblem);
			
			//Generate the variables
			// there will be 1..(unitLines*unitColumns)^2
			generateVariables(describedProblem);
			
			//Generate all the relations?
			//not for now
			
			//Generate all the constraints?
			generateConstraints();
			
			//Get all of the preassigns
			currentNodeList = docElement.getElementsByTagName("preassign");
			interpretPreassign(currentNodeList);
			//in here change values and initial domains
	

		}
		catch (IOException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
		catch (ParserConfigurationException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
		catch (SAXException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
	}


	public constraintProblem getProblem()
	{
		return describedProblem;
	}
	
	private void interpretPresentation(NodeList presentationNodeList)
	{
		if( presentationNodeList.getLength() != 1)
 		{
			System.out.println("Error only expecting one Presentation attribute.  The result of this file is unpredictable");
		}

 		String tempLine;
		tempLine = presentationNodeList.item(0).getAttributes().getNamedItem("name").getNodeValue();
		//set the name of our problem instance
		describedProblem.setName(tempLine);
	}

	private void interpretDimensions(NodeList dimensionNodeList)
	{
		if( dimensionNodeList.getLength() != 1)
 		{
			System.out.println("Error only expecting one Dimension attribute.  The result of this file is unpredictable");
		}

 		String tempLines, tempColumns;
		tempLines = dimensionNodeList.item(0).getAttributes().getNamedItem("lines").getNodeValue();
		tempColumns = dimensionNodeList.item(0).getAttributes().getNamedItem("columns").getNodeValue();
		describedProblem.setDimensions(tempLines, tempColumns);
	}

        		public void parseXMLNoPreassign(String fileName)
	{
			describedProblem = new constraintProblem();
//Integer myint = new Integer(2);
//variableDomains.put(myint, describedProblem);

		DocumentBuilderFactory factoryBuilder;
		DocumentBuilder builder;
		Document doc;
		Element docElement;
		NodeList currentNodeList;


		try
		{
			factoryBuilder  = DocumentBuilderFactory.newInstance( );
			builder = factoryBuilder.newDocumentBuilder();
			doc = builder.parse( fileName );
			docElement = doc.getDocumentElement();

			//Read the presentation properties like name
			currentNodeList = docElement.getElementsByTagName("presentation");
			interpretPresentation(currentNodeList);

			//Get the dimensions
			currentNodeList = docElement.getElementsByTagName("dimensions");
			interpretDimensions(currentNodeList);

			//Generate the domain
			// it will be a list equaling 1..(unitLines*unitColumns)
			generateDomain(describedProblem);

			//Generate the variables
			// there will be 1..(unitLines*unitColumns)^2
			generateVariables(describedProblem);

			//Generate all the relations?
			//not for now

			//Generate all the constraints?
			generateConstraints();

			//Get all of the preassigns
			//currentNodeList = docElement.getElementsByTagName("preassign");
			//interpretPreassign(currentNodeList);
			//in here change values and initial domains


		}
		catch (IOException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
		catch (ParserConfigurationException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
		catch (SAXException e)
		{
			System.out.println("IO ERROR");
			System.out.println(e);
		}
	}
                        
	private void interpretPreassign(NodeList variableNodeList)
	{
		String variableLine;
		String variableColumn;
		
 		for(int i = 0;i < variableNodeList.getLength(); i++)
 		{
 			variableLine = variableNodeList.item(i).getAttributes().getNamedItem("line").getNodeValue();
			variableColumn = variableNodeList.item(i).getAttributes().getNamedItem("column").getNodeValue();
			List preassignedDomain = new LinkedList();

			//assign the value to the variable
			problemVariable meh = (describedProblem.getVariable( variableLine + "," + variableColumn));
			meh.setAssigned (
					variableNodeList.item(i).getAttributes().getNamedItem("value").getNodeValue() );
			
			//fix the initial domain
			preassignedDomain.add(variableNodeList.item(i).getAttributes().getNamedItem("value").getNodeValue());
			(describedProblem.getVariable( variableLine + "," + variableColumn)).setDomain(preassignedDomain);
		}
	}
	
	private void generateDomain(constraintProblem describedProblem)
	{
		problemDomain newDomain = new problemDomain("standardDomain", describedProblem );
			String range;
			int max = describedProblem.linesPerUnit * describedProblem.columnsPerUnit;
			range = "1.." + max;
			newDomain.addRangeToDomain( range );
	}

	private void generateVariables(constraintProblem parentProblem)
	{
 		for(int lineIndex = 1; lineIndex <= (parentProblem.totalLines); lineIndex++)
 		{
 	 		for(int columnIndex = 1; columnIndex <= (parentProblem.totalColumns); columnIndex++)
 	 		{ 	
 	 			problemVariable newVariable = new problemVariable(
				( lineIndex + "," + columnIndex ), describedProblem);
 	 			newVariable.setDomain( "standardDomain" );
 	 		}
 		}
	}

//	private void interpretRelations(NodeList relationNodeList)
//	{
// 		for(int i = 0;i < relationNodeList.getLength(); i++)
// 		{
// 			constraintRelation newRelation = new constraintRelation(
//				relationNodeList.item(i).getAttributes().getNamedItem("name").getNodeValue(), describedProblem);
//			newRelation.setDomains ( relationNodeList.item(i).getAttributes().getNamedItem("domain").getNodeValue() );
//			newRelation.setConflicts ( relationNodeList.item(i).getAttributes().getNamedItem("conflicts").getNodeValue() );
//
//		}
//
//	}

	private void generateConstraints()
	{
 		for(int cellLineIndex = 1; cellLineIndex <= describedProblem.totalLines; cellLineIndex++)
 		{
 	 		for(int cellColumnIndex = 1; cellColumnIndex <= describedProblem.totalColumns; cellColumnIndex++)
 	 		{
 	 			//add the line constraints
 	 			for(int colpointer = cellColumnIndex + 1; colpointer <= describedProblem.totalColumns; colpointer++ )
 	 			{
 	 				new binaryIntensiveConstraint(
 	 						cellLineIndex + "," + cellColumnIndex + " " + cellLineIndex + "," + colpointer,
 	 						describedProblem);
 	 			}
 	 			
 	 			//add the column constraints
 	 			for(int rowPointer = cellLineIndex + 1; rowPointer <= describedProblem.totalLines; rowPointer++ )
 	 			{
 	 				new binaryIntensiveConstraint(
 	 						cellLineIndex + "," + cellColumnIndex + " " + rowPointer + "," + cellColumnIndex, 
 	 						describedProblem);
 	 			} 			
 	 		}
 		}

 		//add the remaining unit constraints
 		//number of units accross is = to describedProblem.linesPerUnit
 		for(int unitLine = 0; unitLine < describedProblem.columnsPerUnit; unitLine++)
 		{
 			for(int unitColumn = 0; unitColumn < describedProblem.linesPerUnit; unitColumn++)
 			{
 				//inside of every unit add constraints at every row and every column but the last one
 				// to avoid repeating constraints the rule is you can look up but not back.
 				for(int unitLineIndex = 1; unitLineIndex <= describedProblem.linesPerUnit; unitLineIndex++)
 				{
 					//this only goes to less than becase of the rule where you don't look back the last row
 					//will add no constraints
 					for(int unitColumnIndex = 1; unitColumnIndex < describedProblem.columnsPerUnit; unitColumnIndex++)
 					{
						//since we don't mind looking up we set the line pointer @ 0
 						for(int linePointerIndex = 1; linePointerIndex <= describedProblem.linesPerUnit; linePointerIndex++)
 						{
 							//since we don't look back we set the column pointer @ +1
 							for(int columnPointerIndex = unitColumnIndex + 1; columnPointerIndex <= describedProblem.columnsPerUnit; columnPointerIndex++)
 							{
 								//make sure we are not repeating the same line
 								if(linePointerIndex != unitLineIndex)
 								{
	 								//now we add the constraints
	 			 	 				new binaryIntensiveConstraint(
	 			 	 						((unitLine * describedProblem.linesPerUnit) + unitLineIndex) + "," + 
	 			 	 						((unitColumn * describedProblem.columnsPerUnit) + unitColumnIndex) + " " + 
	 			 	 						((unitLine * describedProblem.linesPerUnit) + linePointerIndex) + "," + 
	 			 	 						((unitColumn * describedProblem.columnsPerUnit) + columnPointerIndex),
	 			 	 						describedProblem);
 								}
 							}
 						}
 					}
 				}
 			}
 		}
 			
		//add the All diff constraints.
		//start with the lines
 		for(int cellLineIndex = 1; cellLineIndex <= describedProblem.totalLines; cellLineIndex++)
 		{
 			String variablesAffected = "";
 			
 	 		for(int cellColumnIndex = 1; cellColumnIndex <= describedProblem.totalColumns; cellColumnIndex++)
 	 		{

 	 				variablesAffected = variablesAffected + cellLineIndex + "," + cellColumnIndex + " ";
 	 		}	 	 			
 	 			variablesAffected.trim();
 	 			new nonBinaryIntensiveConstraint( variablesAffected,
 	 					describedProblem, "L" + cellLineIndex); 	 	 			
 		}
 		
		//do the columns
 		for(int cellColIndex = 1; cellColIndex <= describedProblem.totalLines; cellColIndex++)
 		{
 			String variablesAffected = "";
 			
 	 		for(int cellLineIndex = 1; cellLineIndex <= describedProblem.totalColumns; cellLineIndex++)
 	 		{

 	 				variablesAffected = variablesAffected + cellLineIndex + "," + cellColIndex + " ";
 	 		}	 	 			
 	 			variablesAffected.trim();
 	 			new nonBinaryIntensiveConstraint( variablesAffected,
 	 					describedProblem, "C" + cellColIndex); 	 	 			
 		}
 		
 		//add the unit constraints
 		//number of units accross is = to describedProblem.linesPerUnit
 		for(int unitLine = 0; unitLine < describedProblem.columnsPerUnit; unitLine++)
 		{
 			for(int unitColumn = 0; unitColumn < describedProblem.linesPerUnit; unitColumn++)
 			{
	 			String variablesAffected = "";

 				for(int unitLineIndex = 1; unitLineIndex <= describedProblem.linesPerUnit; unitLineIndex++)
 				{
 					for(int unitColumnIndex = 1; unitColumnIndex <= describedProblem.columnsPerUnit; unitColumnIndex++)
 					{
	 	 				variablesAffected = variablesAffected + (unitLine*describedProblem.linesPerUnit + unitLineIndex)
	 	 					+ "," + (unitColumn*describedProblem.columnsPerUnit + unitColumnIndex) + " ";	
 					}
 				}
	 	 		variablesAffected.trim();
		 	 	new nonBinaryIntensiveConstraint( variablesAffected,
		 	 			describedProblem, "U" + (unitLine+1) + "," + (unitColumn+1));
 			}
 		}
		
// 			problemConstraint newConstraint = new problemConstraint(
//				constraintNodeList.item(i).getAttributes().getNamedItem("scope").getNodeValue(), describedProblem);
//			newConstraint.setRelation ( constraintNodeList.item(i).getAttributes().getNamedItem("relation").getNodeValue() );
	}

}