/*
 * Created on 28.11.2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package de.farafin.snEADy.inOut;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import de.farafin.snEADy.communication.D_Level;
import de.farafin.snEADy.communication.I_Constants;


/**
 * This class loads level files. 
 * @author roland, lars
 * 
 * @see java.io.FileReader
 *
 * @version $Revision: 1.17 $
 */
public final class C_LevelLoader implements I_Constants, I_LevelLoader_interface
{

	/**
	 * if a string has a coment tag '%' inside, than only the part before its first accuence will
	 * be taken and returned. 
	 * 
	 * @param str an string
	 * @return the beginning of <code>str</code> befre the first '%'
	 */
	private String removeComents(String str)
	{
		String result[] = null;
		result = str.split("%");
		return result[0];
	}
	

	/**
	 * removes ONLY leading spaces and spaces at the end. spaces in the middle of the string are ignored
	 * 
	 * @param str
	 * @return the clear string
	 */
	private String removeSpaces(String str)
	{
		String result = null;
		if(str == null) return null;
		if(str.length() < 1) return str;
		
		result = str;
		// delete all tab and write spaces instead
		if(result.length() > 0) result = result.replaceAll("\t", " ");
		// delates all spaces from the beginning
		while(result.charAt(0) == ' ') result = result.substring(1);
		// delates all spaces from the end
		while(result.charAt(result.length()-1) == ' ') result = result.substring(0, result.length()-1);
		
		return result;
	}
	
	
	/** This method loads the level. All other funktions are rooting in here.
	 * 
	 * @param root the realtive root directory to snEADy mainfolder 
	 * @param levName The name of the level that should be loaded
	 * @param loadPlayfield  either the Playfield should be loaded or not.
	 * @return the level, if loadPlayfield == false, playFiled will be null.
	 * @throws FileNotFoundException
	 * @throws E_LevelFileException
	 */
	private D_Level load(String root, String levName, boolean loadPlayfield) throws FileNotFoundException, E_LevelFileException
	{

		D_Level level = null;
		String filePath = null;
		String filename = null;
		
		File file = null;
		FileReader fileReader = null;
		BufferedReader bufferedReader = null;
		
		String argument = null;
		String str1 = null;
		String strArray[] = null;
		
		int i;
		boolean sizeAccured = false;
				
		if(DEBUG) System.out.println("DEBUG C_LevelLoader: loadLevel(" + root + ", " + levName + ")");

		// construction of the full file name
		//filePath = root + "." + levName;		
		//filename = filePath.replace('.', File.separatorChar) + ".smp";
		filePath = root.replace('.', File.separatorChar);
		filename = levName + ".smp";
		
		// read the file as characters
		try
		{
			// the idea is to read a file line by line.
			// for creating a lineReader, its necessary to have a Reader Object, which superclass of FileReader
			// But FileReader needs a file as input, so the way we go is:
			
			// first make a file
			file = new File(filePath, filename);
			// second creating a file reader
			fileReader = new FileReader(file);
			// thired create the buffered Reader
			bufferedReader = new BufferedReader(fileReader);
		}
		catch(FileNotFoundException e)
		{
			if(DEBUG) System.out.println("WARNING C_LevelLoader: File " + filename + " not found!\n\t"  + e.toString());

			return null;
		}
		
		// create the level with 
		level = new D_Level();
		level.height = 0;
		level.width = 0;
		level.fileName = levName;
		
		try
		{
			// now i have with readLine() a great tool for reading a textFile :)
			
			// read the compleate file and fill the level-opbject.
			while(bufferedReader.ready())
			{
				str1 = bufferedReader.readLine();
				// compate the first characters of the line to decide which command is written.
				
				if(str1.startsWith("<name>"))
				{
					// takes the string from the fifth index on
					argument = str1.substring(6);

					// clean the string
					argument = removeComents(argument);
					argument = removeSpaces(argument);
					
					level.name = argument;

					//if(DEBUG) System.out.println("DEBUG C_LevelLoader: name: \"" + level.name + "\"");	
				}
				if(str1.startsWith("<comment>"))
				{
					level.comment = "";
					
					// takes the string from the fifth index on
					
					argument = str1.substring(9);

					// clean the string
					argument = removeComents(argument);
					argument = removeSpaces(argument);
					
					level.comment += argument;
					
					while(bufferedReader.ready())
					{
						argument = bufferedReader.readLine();
						if(argument.startsWith("<endcomment>")) break;

						if (argument.length()>0)level.comment += '\n';
						
						argument = removeComents(argument);
						argument = removeSpaces(argument);
						
						level.comment += argument;
					}
				}

				if(str1.startsWith("<autor>"))
				{
					argument = str1.substring(7);

					// clean the string
					argument = removeComents(argument);
					argument = removeSpaces(argument);
					
					level.autor = argument;
				}
				
				else if(str1.startsWith("<size>"))
				{
					// takes the string from the fifth index on
					argument = str1.substring(6);
					
					// clean the string
					argument = removeComents(argument);
					argument = removeSpaces(argument);

					// cntrol if only allowed characters are used
					if(!argument.matches("[[0-9][x]]*"))
					{
						try{ bufferedReader.close(); fileReader.close();}
						catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
						throw new E_LevelFileException("The size-tag \"" + argument + "\" of the file " + file + " is wrong formated!\n");
					}
					// splitting the argument into height and width.
					strArray = argument.split("x");

					// and again: if not exactly on x is in the tag, it accures an exception 
					if(strArray.length != 2)
					{
						try{ bufferedReader.close(); fileReader.close();}
						catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
						throw new E_LevelFileException("The size-tag \"" + argument + "\" of the file " + file + " has too many subparameters!\n");
					}

					// set parameter
					level.height = Integer.parseInt(strArray[0]);
					level.width = Integer.parseInt(strArray[1]);
					
					//if(DEBUG) System.out.println("DEBUG C_LevelLoader: width: " + level.width);
					//if(DEBUG) System.out.println("DEBUG C_LevelLoader: height: " + level.height);
					
					sizeAccured = true;
				}

				else if(str1.startsWith("<maxPlayer>"))
				{
					// takes the string from the fifth index on
					argument = str1.substring(11);

					// clean the string
					argument = removeComents(argument);
					argument = removeSpaces(argument);
					
					level.maxPlayer = Integer.decode(argument).intValue();

					//if(DEBUG) System.out.println("DEBUG C_LevelLoader: maxPlayer: " + level.maxPlayer);
				}
				else if(str1.startsWith("<playField>"))
				{
					if(!loadPlayfield)
					{
						break;
					}
					//if(DEBUG) System.out.println("DEBUG C_LevelLoader: playField:");
					// if the size of the playField is unknown, its not possible to set the array
					// to the right size. thats why the size tag must be writteb before the
					// the playField accures
					if(!sizeAccured)
					{
						try{ bufferedReader.close(); fileReader.close();}
						catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
						throw new E_LevelFileException("The playField-tag was written before the size-tag!\n");
					}
					
					// set array
					level.playField = new char[level.height][];
					
					// load linewhise the playField into the playField array
					//for(i=0;i<level.height; i++)
					i = 0;
					while(bufferedReader.ready())
					{
						// if the file ends before the given number of lines is read, a error accures.
						/*if(!bufferedReader.ready())
						{
							try{ bufferedReader.close(); fileReader.close();}
							catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
							throw new E_LevelFileException("The playField-repesentation is too short, it must be at size of height by height!\n");
						}*/
						// read one line						
						str1 = bufferedReader.readLine();
						
						// if the playfield is over, stop the process
						if(str1.startsWith("<endPlayfield>")) break;
							
						// clean the string
						str1 = removeComents(str1);
						str1 = removeSpaces(str1);

						if(i >= level.height)
						{
							try{ bufferedReader.close(); fileReader.close();}
							catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
							throw new E_LevelFileException("There are too many lines in the playfield. It must be the same as it is defined in size: " + level.height + "\n");
						}
						
						// control length of the string
						if(str1.length() != level.width)
						{
							try{ bufferedReader.close(); fileReader.close();}
							catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
							throw new E_LevelFileException("The playField-repesentation of line " + i + " is wrong. Its length(" + str1.length() +") must be the same as width: " + level.width + "!\n");
						}
											
						// control characters
						if(!str1.matches("[[0-9][a-z][A-Z][.#]]*"))
						{
							try{ bufferedReader.close(); fileReader.close();}
							catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
							throw new E_LevelFileException("The playfild-line " + i + " of the file " + file + " contains unexpected characters!\n");
						}

						// finaly set array
						level.playField[i] = str1.toCharArray();
						//if(DEBUG) System.out.println("DEBUG C_LevelLoader: " + String.valueOf(level.playField[i]));
						i++;
					}
					
					if(i < level.height)
					{
						try{ bufferedReader.close(); fileReader.close();}
						catch(IOException e){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
						throw new E_LevelFileException("There are too less lines in the playfield: " + i+1 + ". It must be the same as it is defined in size: " + level.height + "\n");
					}
				}
			}

			bufferedReader.close();
			fileReader.close();

			// control if all tags were loaded:
			if(level.name == null) {throw new E_LevelFileException("The name-tag of the file " + file + " is missing!\n");}
			if(level.width == 0)  {throw new E_LevelFileException("The size-tag of the file " + file + " is missing or height is set to 0!\n");}
			if(level.height == 0) {throw new E_LevelFileException("The size-tag of the file " + file + " is missing or width is set to 0!\n");} 
			if(level.maxPlayer == 0) {throw new E_LevelFileException("The maxPlayer-tag of the file " + file + " is missing or set to 0!\n");}
			if(level.playField == null && loadPlayfield) {throw new E_LevelFileException("The playField-tag of the file " + file + " is missing!\n");}
						
			//if(DEBUG) System.out.println("DEBUG C_LevelLoader: loaded Level:\n\n" + level.toString());
		}
		// happends if something is wrong with the java-file-stream

		catch(FileNotFoundException e)
		{
			throw e;
		}
		catch(E_LevelFileException e)
		{
			throw e;
		}
		catch(IOException e)
		{
			if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);
		
			try{ bufferedReader.close();}
			catch(IOException e2){if(DEBUG) System.out.println("WARNING C_LevelLoader: " + e);}
			return null;
		}
		
		if(DEBUG) System.out.println("DEBUG C_LevelLoader: File successful loaded!");
		return level;
	}
	
	
	/* (non-Javadoc)
	 * @see de.farafin.snEADy.inOut.I_LevelLoader_interface#loadLevelHeader(java.lang.String, java.lang.String)
	 */
	public D_Level loadLevelHeader(String root, String levName) throws E_LevelFileException, FileNotFoundException
	{
		return load(root, levName, false);
	}
	/* (non-Javadoc)
	 * @see de.farafin.snEADy.inOut.I_LevelLoader_interface#loadLevelHeader(java.lang.String)
	 */
	public D_Level loadLevelHeader(String levName) throws E_LevelFileException, FileNotFoundException
	{
		return load("levels", levName, false);
	}
	
	/* (non-Javadoc)
	 * @see GameParameter.I_LevelLoader_interface#loadLevel(java.lang.String, java.lang.String)
	 */
	public D_Level loadLevel(String root, String levName)  throws E_LevelFileException, FileNotFoundException
	{ 
		return load(root, levName, true);
	}

	/* (non-Javadoc)
	 * @see GameParameter.I_LevelLoader_interface#loadLevel(java.lang.String)
	 */
	public D_Level loadLevel(String levName) throws E_LevelFileException, FileNotFoundException
	{
		return load("levels", levName, true);
	}
}