import java.io.*;
import java.util.Scanner;

/*
 * File: ConstraintSimulator.java
 * Author: Dillon Sadofsky
 *
 * Description:	This is meant to be a command line program that reads in data from a file that
 *	represents a set of variables, and constraints between those variables.  This data is
 *	parsed, checked, and put into a constraint tuple, then it is checked for satisfiability.
 *		For proper input file formatting, see Documentation.txt or the sample In.dat file.
 */

public class ConstraintSimulator
{
	public static void main (String[] args)
	{
		System.out.print("\nWelcome to the Constraint Simulator.\nThis program will accept a file with a set of n variables, then a set of constraints\n");
		System.out.print("on those variables.\n\n");

		System.out.println("Please enter the input file's filename:");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		String strFileName = null;
		try
		{
			strFileName = in.readLine();
		}
		catch (IOException ioe)
		{
			strFileName = "In.dat";
			System.out.println("There was a problem getting the input filename, attempting to move forward with default (In.dat)");
		}

		System.out.println("\nAttempting to open the file: '" + strFileName + "'");

		// Attempt to open the file
		File file;
		file = new File(strFileName);

		if (!file.exists())
		{
			System.out.println("The file was not found, please create input file and try again.");
			return;
		}

		// Make a scanner for easy reading
		Scanner scan;
		try
		{
			scan = new Scanner(file);
		}
		catch (FileNotFoundException e)
		{
			System.out.println("Failed to open the file with the scanner.");
			return;
		}

		System.out.println("File opened sucessfully, beginning read");

		// Use the semicolon as line seperator
		scan.useDelimiter(";");

		// Make sure that there is a line to read
		if (!scan.hasNextLine())
		{
			System.out.println("No first line of variables detected, read aborted");
			return;
		}

		// Create a scanner for the first line
		Scanner scnTokenScanner = new Scanner(scan.nextLine());
		scnTokenScanner.useDelimiter("[,;]");
		CdbTuple data = new CdbTuple();
		int nCnt = 0, nRows = 0;

		// Grab all the variables and push them in
		while (scnTokenScanner.hasNext())
		{
			String name = scnTokenScanner.next().trim();
			data.AddVariable(name + "+");
			data.AddVariable(name + "-");
			nCnt+=2;
		}
		scnTokenScanner.close();

		// Now, initialize the matrix so that we can pump data into it
		try
		{
			data.InitializeMatrix(nCnt);
		}
		catch (Exception e)
		{
			System.out.println("Unable to initialize the array to size " + nCnt + " aborting program");
			return;
		}

		// On each line, there will be the two vertices and the weight
		String strVar1, strVar2, strSign1, strSign2;
		int nLocation1, nLocation2;
		double nWeight;

		// While there are more lines to get, scan them in and get data out of them
		while (scan.hasNextLine())
		{
			String strTemp = scan.nextLine();
			scnTokenScanner = new Scanner(strTemp);
			scnTokenScanner.useDelimiter("[;,]");

			// Make sure the format is right - unfortunately, this doesn't work for some reason - all pattern matching seems to always return false
			/*if (!Pattern.matches("^[.*,]{3};$", strTemp))
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' format invalid.  Expected: \"variable, variable, weight;\"");
				return;
			}*/
			
			// Make sure there is a first sign
			if (!scnTokenScanner.hasNext())
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' first clause format invalid.  Expected: \"sign1, variable1, sign2, variable2, weight;\" meaning (sign1)var1 - (sign2)var2 >= weight");
				return;
			}
			strSign1 = scnTokenScanner.next().trim();
			
			// Make sure there is a first clause
			if (!scnTokenScanner.hasNext())
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' second clause format invalid.  Expected: \"sign1, variable1, sign2, variable2, weight;\" meaning (sign1)var1 - (sign2)var2 >= weight");
				return;
			}
			strVar1 = scnTokenScanner.next().trim();
			
			// Make sure there is a second sign
			if (!scnTokenScanner.hasNext())
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' third clause format invalid.  Expected: \"sign1, variable1, sign2, variable2, weight;\" meaning (sign1)var1 - (sign2)var2 >= weight");
				return;
			}
			strSign2 = scnTokenScanner.next().trim();

			// Make sure there is a second clause
			if (!scnTokenScanner.hasNext())
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' fourth clause format invalid.  Expected: \"sign1, variable1, sign2, variable2, weight;\" meaning (sign1)var1 - (sign2)var2 >= weight");
				return;
			}
			strVar2 = scnTokenScanner.next().trim();

			// Make sure there is a third clause
			if (!scnTokenScanner.hasNext())
			{
				System.out.println("Unable to read constraint line: " + nRows + ": '" + strTemp + "' fifth clause format invalid.  Expected: \"sign1, variable1, sign2, variable2, weight;\" meaning (sign1)var1 - (sign2)var2 >= weight");
				return;
			}
			nWeight = Double.parseDouble(scnTokenScanner.next());
			
			if ((!strSign1.equals("+") && !strSign1.equals("-")) || (!strSign2.equals("+") && !strSign2.equals("-")))
			{
				System.out.println("One of the sign clauses (" + strSign1 + ", " + strSign2 + ") was something other than + or -, please make sure your format was right");
				return;
			}
			
			// If both signs are -'s, then make them both +'s (regular difference constraint) in the opposite direction
			if (strSign1.equals("-") && strSign2.equals("-"))
			{
				String temp = strVar2;
				strVar2 = strVar1;
				strVar1 = temp;
				
				strSign1 = strSign2 = "+";
			}

			// Translate the strings into array locations
			nLocation1 = data.GetStringIndex(strVar1 + strSign1);
			nLocation2 = data.GetStringIndex(strVar2 + strSign2);

			// If we found both items, add this constraint
			if (nLocation1 != -1 && nLocation2 != -1)
			{
				//data.AddConstraint(nLocation1, nLocation2, nWeight);
				data.AddConstraint(nLocation2, nLocation1, nWeight);
			}
			else
			{
				System.out.println("One of the variables: " + strVar1 + ", " + strVar2 + " was not found in the array. Bad input.");
				return;
			}
			
			// Also if this is an addition constraint (has one - and one +), add a reverse edge as well
			if (!strSign1.equals(strSign2))
			{
				nLocation1 = data.GetStringIndex(strVar1 + strSign2);
				nLocation2 = data.GetStringIndex(strVar2 + strSign1);
				
				// If we found both items, add this constraint
				if (nLocation1 != -1 && nLocation2 != -1)
				{
					//data.AddConstraint(nLocation1, nLocation2, nWeight);
					data.AddConstraint(nLocation1, nLocation2, nWeight);
				}
				else
				{
					System.out.println("One of the variables: " + strVar1 + ", " + strVar2 + " was not found in the array. Bad input.");
					return;
				}
			}

			scnTokenScanner.close();

			nRows++;
		}

		// Make sure to clean up
		scan.close();

		//data.Trace();

		// Print output to user
		System.out.println("\n" + nRows + " constraints successfully read into tuple\n\nIs the set of addition constraints satisfiable?");
		System.out.println((data.satisfiable() ? "Yes" : "No") + "\n");
	}
}
