package latte.lattice;

import java.io.*;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LatticeFileReader {

	public static class LatticeFileToken {
		Lattice lattice;
		int type, action;

		public LatticeFileToken(int _type, int _action,
				Lattice _lattice) {
			type = _type;
			action = _action;
			lattice = _lattice;
		}
		public String toString() {
			return "Type: "+type+", Action: "+action;
		}
	}

	/**
	 * represents any exception that can occur while reading the lattice file.
	 */
	public static class LatticeFileException extends Exception {
		protected static final long serialVersionUID = 1L;

		public LatticeFileException(String message) {
			// TODO Auto-generated constructor stub
			super(message);
		}
	}

	/** actionEnd: doesn't distinguish between OR or SEQ. */
	public boolean restrictionAllowMissingEndOfBlock = true,
			restrictionEndOfBlockIsIdentical = true;

	public final static int actionStart = 0, actionEnd = 1;

	public static Pattern startSeqPattern, startOrPattern, endSeqPattern,
			endOrPattern, wholeWrdPattern, commentPattern;
	// This block should be overridden in every sub-class:
	static {
		startSeqPattern = Pattern.compile("^\\s*\\(\\s*$");
		startOrPattern = Pattern.compile("^\\s*[\\(;]\\s*$");
		endSeqPattern = Pattern.compile("^\\s*\\)\\s*$");
		endOrPattern = Pattern.compile("^\\s*[\\);]\\s*$");
		wholeWrdPattern = Pattern
				.compile("^\\s*\\(\\s*WRD\\s*[^\\)]+\\s*\\)\\s*$");
		commentPattern = Pattern.compile("^\\s*#[^\\n]*\\n$");
	}

	protected Scanner scanner = null;
	protected LinkedList<LatticeFileToken> tokens = new LinkedList<LatticeFileReader.LatticeFileToken>();;

	public LatticeFileReader() {
	}

	public LatticeFileReader(String fileName) {
		try {
			read(fileName);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public boolean hasNext() {
		if (tokens == null) {
			throw new IllegalStateException("Cannot use LatticeFileReader right now because 'tokens' is not initialized.");
		}
		return (!tokens.isEmpty());
	}

	public LatticeFileToken getNext() {
		if (tokens == null) {
			throw new IllegalStateException("Cannot use LatticeFileReader right now because 'tokens' is not initialized.");
		}
		return tokens.poll();
	}

	protected boolean hasErrors = false;

	public boolean read(String fileName) {

		// Lattice lattice = new Lattice();
		
		log("Reading from file.");
		StringBuilder text = new StringBuilder();
		String NL = System.getProperty("line.separator");
		int lineNum = 0;
		try {
			try {
				scanner = new Scanner(new FileInputStream(fileName));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				throw new LatticeFileException("Cannot find file:\n\t"
						+ fileName);
			}
			while (scanner.hasNextLine()) {
				text.append(scanner.nextLine() + NL);

			}
			while (text.length() > 0) {
				lineNum++;
				Matcher m;
				if ((m = startSeqPattern.matcher(text.toString())).find()) {
					//Test.println("Matcher: Start SEQ: " + m.group());
					// text.
					tokens.add(new LatticeFileToken(Lattice.SEQ,
							actionStart, new Lattice(Lattice.SEQ)));
				}
				else if ((m = startOrPattern.matcher(text.toString())).find()) {
					//Test.println("Matcher: Start OR: " + m.group());
					tokens.add(new LatticeFileToken(Lattice.OR,
							actionStart, new Lattice(Lattice.OR)));
				} else if ((m = wholeWrdPattern.matcher(text.toString())).find()) {
					//Test.println("Matcher: Start WRD: " + m.group());
					tokens.add(new LatticeFileToken(Lattice.WRD,
							actionStart, new Lattice(Lattice.WRD,
									parseElement(m.group()))));
					//Test.println("\n\n>>>\t\t HI <<<<\n\n");
				}
				else if ((m = endSeqPattern.matcher(text.toString())).find()) {
					//Test.println("Matcher: End SEQ: " + m.group());
					tokens.add(new LatticeFileToken(Lattice.SEQ,
							actionEnd, null));
				} else if ((m = endOrPattern.matcher(text.toString())).find()) {
					//Test.println("Matcher: End OR: " + m.group());
					tokens.add(new LatticeFileToken(Lattice.OR,
							actionEnd, null));
				}
				else if ((m = commentPattern.matcher(text.toString())).find()) {
					// Ignore:
					//Test.println("Matcher: Comment: " + m.group());
					// continue;
				} else { // Illegal line:
					throw new LatticeFileException(
							"Illegal line in the Lattice file:\n\t"
									+ text.substring(0, 100));
				}
				int len = m.group().length();
				//Test.println("\nLEN: "+len);
				text.delete(0, len);
			}
			//Test.println("\n\t\t[[[[DONE]]]] \n");
			/*while (!tokens.isEmpty()) {
				//Test.println(tokens.poll().toString());
			}*/
		} catch (LatticeFileException lfe) {
			System.err.println("Error in Lattice File (line: " + lineNum
					+ ") with message:\n" + lfe.getMessage());
			hasErrors = true;

		} finally {
			scanner.close();
		}
		return hasErrors;
	}

	public void write(String fileName, Lattice lattice)
			throws IOException {
		log("Writing to file named " + fileName);
		Writer out = new OutputStreamWriter(new FileOutputStream(fileName));
		try {
			out.write(lattice.toString());
		} finally {
			out.close();
		}

	}

	public Element parseElement(String string) {
		// To be inherited and written to the specific Element type.
		return new Element(string);
	}

	protected void log(String aMessage) {
		System.out.println(aMessage);
	}
}
