import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

/**
 * Methods needed to read and write the puzzle
 */

public class PuzzleInputOutput 
{
	private static String[][] puzzleStart;
	private static String[][] puzzleDestination;
	
	public final static int PARSERSTATE_LVLSIZE = 0;
	public final static int PARSERSTATE_STARTCONFIG = 1;
	public final static int PARSERSTATE_FINALCONFIG = 2;
	public final static int PARSERSTATE_PARSINGEND = 3;
	
	public final static String FORMAT_CONFIGSEPERATOR = "---";
	public final static String FORMAT_FREEFIELD = "#";
	
	/**
	 * This method reads the data from the file and 
	 * initialises the Start and Destination states of the Puzzle
	 * 
	 * @param puzzleFile
	 * @throws IOException
	 */
	public PuzzleInputOutput(String puzzleFile) throws IOException
	{
		//open file
		BufferedReader reader = new BufferedReader(new FileReader(puzzleFile));
		
		//var to store a line temporary
		String line;
		//var to store size of puzzle temporary
		int size = 0;
		//state of the parsing
		int state = PARSERSTATE_LVLSIZE;
		//state of the configuration parsing
		int config_state = 0;
		//state which indicates if free field was seen in actual configuration 
		boolean free_field = false;
		//do we need a linebreak?
		boolean linebreak = true;
		//last row in array to force a linebreak
		int last_line = 0;
		
		//read until fileend
		while((line = reader.readLine()) != null)
		{
			//check if an linebreak was expected
			if(!linebreak)
			{
				throw new PuzzleFileFormatException("Unexpected Linebreak in Puzzlefile");
			}
			
			linebreak = false;
			
			//tokenize the input string
			StringTokenizer st = new StringTokenizer(line);
			while(st.hasMoreTokens())
			{
				//linbreak not found
				if(linebreak)
				{
					throw new PuzzleFileFormatException("Expected Linebreak in Puzzlefile, but could not find it");
				}
				
				switch(state)
				{
					//parse levelsize
					case PARSERSTATE_LVLSIZE:
					{
						//read size
						try
						{
							size = Integer.valueOf(st.nextToken());
						} catch(NumberFormatException e)
						{
							throw new PuzzleFileFormatException("Levelsizetoken was not a number");
						}
						
						//create arrays
						puzzleStart 		= new String[size][size];
						puzzleDestination 	= new String[size][size];
						
						//change state of parser
						state = PARSERSTATE_STARTCONFIG;
		
						//expect linebreak
						linebreak = true;
						break;
					}
					
					//parse configurations
					case PARSERSTATE_STARTCONFIG:
					case PARSERSTATE_FINALCONFIG:
					{
						// still reading config?
						if(config_state < size*size)
						{
							String token = st.nextToken();
							
							//save last linenumber
							last_line = config_state/size;
							
							//fill in token
							if(state == PARSERSTATE_STARTCONFIG){
								puzzleStart[config_state/size][config_state%size] = token;
							} else {
								puzzleDestination[config_state/size][config_state%size] = token;
							}
							
							//free field?
							if(token.equals(FORMAT_FREEFIELD))
							{
								//more than one free field
								if(free_field)
									throw new PuzzleFileFormatException("Puzzle file contained more then one free Field in one Configuration");
								
								free_field = true;
							}
							
							//next part in array
							config_state++;
							
							//last line
							if(config_state/size == size)
							{
								//No free field?
								if(!free_field)
									throw new PuzzleFileFormatException("Puzzle file contained no free Field in one Configuration");
							}
							
							//do we need a linebreak here?
							if(last_line != config_state/size)
							{
								linebreak = true;
							}
						} else
						{
							//PuzzleFile Seperator
							if(state == PARSERSTATE_STARTCONFIG && !st.nextToken().equals(FORMAT_CONFIGSEPERATOR)){
									throw new PuzzleFileFormatException("Puzzle file ConfigurationSeperator was not found");
							}
							
							//reset vars
							free_field = false;
							config_state = 0;
							
							//parse finalconfig after startconfig
							if(state == PARSERSTATE_STARTCONFIG){
								state = PARSERSTATE_FINALCONFIG;
								
								linebreak = true;
							} else {
								//No more characters or linebreaks expected!
								state = PARSERSTATE_PARSINGEND;
							}
						}
						break;
					}
					
					default:
						throw new PuzzleFileFormatException("Puzzle file contains more lines after finalconfiguration");
				}
			}
		}
	}
	/**
	 * It prints the given puzzle state
	 * 
	 * @param puzzle the puzzle
	 */
	public static void printPuzzle(String[][] puzzle)
	{
		if(puzzle == null)
		{
			System.out.println("Puzzle given was null");
		}
	
		String output = new String();
		
		for(int i=0; i < puzzle.length; i++)
		{
			for(int j=0; j < puzzle[i].length; j++)
			{
				output += puzzle[i][j] + "\t";
			}			
			output += "\n";
		}
		
		System.out.println(output);
	}
	
	/**
	 * @return the puzzleStart
	 */
	public String[][] getPuzzleStart() {
		return puzzleStart;
	}
	/**
	 * @return the puzzleDestination
	 */
	public String[][] getPuzzleDestination() {
		return puzzleDestination;
	}
}
