package javakurs.musterloesung.uebung10;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;

import javakurs.musterloesung.uebung08.Edge;

/**
 * {@link LexFsa} extends {@link FiniteAutomaton} to be used as a lexicon
 * automaton.
 *
 *
 * @author Joerg Steffen, DFKI
 * @version $Id: LexFsa.java,v 1.1 2011/01/19 09:37:00 steffen Exp $
 */
public class LexFsa extends FiniteAutomaton<Character> {

  /**
   * This contains the comparator used to compare the character edges of the
   * automaton.
   */
  private static Comparator<Character> charComp =
    new Comparator<Character>() {

    /**
     * {@inheritDoc}
     */
    @Override
    public int compare(Character arg1, Character arg2) {

      return arg1.charValue() - arg2.charValue();
    }
  };


  /**
   * This creates a new instance of {@link LexFsa}.
   */
  public LexFsa() {

    super();
    super.setComparator(charComp);
  }


  /**
   * This adds the given word to the language of the automaton.
   *
   * @param word
   *          the word to add
   */
  private void addWord(String word) {

    int current = this.getInitialState();
    for (int i = 0; i < word.length(); ++i) {
      List<Edge<Character>> outEdges =
        this.graph.findEdges(current, word.charAt(i), charComp);
      int nextState = -1;
      if (outEdges.isEmpty()) {
        nextState = this.createNewState();
        this.createNewTransition(word.charAt(i), current, nextState);
      }
      else {
        // since the automaton is deterministic, there is only one next node
        nextState = outEdges.get(0).getEndVertex();
      }
      current = nextState;
    }
    this.setFinalState(current);
  }


  /**
   * This creates a deterministic automaton from the words in the given file.
   *
   * @param filename
   *          the file name of the word list
   * @return the deterministic automaton
   * @throws IOException
   *           if there is an error when building the automaton
   */
  public static LexFsa buildLexiconAutomaton(String filename)
      throws IOException {

    BufferedReader in =
      new BufferedReader(
        new FileReader(filename));
    LexFsa fsa = new LexFsa();
    fsa.setInitialState(fsa.createNewState());
    String line = null;
    while ((line = in.readLine()) != null) {
      fsa.addWord(line);
    }
    in.close();

    return fsa;
  }


  /**
   * This tries to match the given string against the language of the automaton.
   *
   * @param input
   *          the string to match
   * @return a flag indicating a match
   */
  public boolean match(String input) {

    // this keeps track of the current state while traversing the automaton
    int current = this.getInitialState();
    for (int i = 0; i < input.length(); ++i) {
      char currChar = input.charAt(i);
      List<Edge<Character>> nextNodes =
        this.graph.findEdges(current, currChar, charComp);
      if (nextNodes.isEmpty()) {
        return false;
      }
      // since the automaton is deterministic, there is only one next node
      // (if any)
      current = nextNodes.get(0).getEndVertex();
    }

    // check if we have reached a final state after processing the whole input
    return this.isFinalState(current);
  }
}
