package de.visone.nlpext.network;

import java.util.IdentityHashMap;
import java.util.List;

import atomicwheel.Node;
import de.visone.ext.nlp.TextPosition;
import edu.stanford.nlp.ling.HasWord;
import edu.stanford.nlp.parser.lexparser.LexicalizedParser;
import edu.stanford.nlp.parser.lexparser.LexicalizedParserQuery;
import edu.stanford.nlp.trees.Tree;

/**
 * This class handles the parsing of the sentences and the assigning of word
 * positions.
 * 
 * @author nick
 */
public abstract class ParserNetwork extends TextNetwork {

	// TODO disable logging
	private static boolean logging = false;

	// the parser that is used
	private final LexicalizedParserQuery myParserQuery;
	protected String parserDescription = null;

	// A Map with Trees as keys is used because indexing the words before
	// parsing does not work (as the String-objects in the sentence may not
	// be same as the ones in the parse-tree)
	// An IdentityHashMap (using reference-equality in place of
	// object-equality) is used because for some subtrees t1 and t2 the
	// expression t1.equals(t2) may be true (Tree objects are equal if they
	// have equal Labels, the same number of children, and their children
	// are pairwise equal.)
	private IdentityHashMap<Tree, Integer> positions;;

	private int currentSentenceIndex;

	// ========== constructor ==========

	/**
	 * @param parser
	 *            the parser to use
	 * @param parserDescription
	 *            a description of the parser
	 * @param layout
	 *            <code>true</code> to do a quick layout after creating the
	 *            network
	 */
	public ParserNetwork(final LexicalizedParser parser,
			final String parserDescription, final boolean layout) {
		super(layout);
		if (parser == null)
			throw new IllegalArgumentException("Parser must not be null!");
		myParserQuery = parser.parserQuery();
		this.parserDescription = parserDescription;
		positions = new IdentityHashMap<Tree, Integer>();
	}

	@Override
	protected void processSentence(final List<? extends HasWord> sentence,
			final int sentenceIndex) {
		currentSentenceIndex = sentenceIndex;

		// parse the sentence and get the best parse
		try {
			myParserQuery.parse(sentence);
			final Tree tree = myParserQuery.getBestParse();

			assignWordPositions(tree);

			if (logging) {
				System.out.println("\n(" + sentenceIndex + ") "
						+ sentence.toString() + "\n" + tree.toString() + "\n");
			}

			processSentence(tree);

		} catch (final java.lang.UnsupportedOperationException e) {

			// parse is not possible if sentence is too long
			message = e.getMessage()
					+ "ParserNetwork.processSentence: SENTENCE SKIPPED! ("
					+ sentenceIndex + ") " + sentence.toString();

		}
	}

	/**
	 * since parser networks make use of a parser, each sentence will be
	 * processed as a tree after it was parsed
	 */
	protected abstract void processSentence(Tree tree);

	/**
	 * Adds the word held by the leaf of a ParseTree after assigning the text
	 * position. Attention: A leaf has to be added during the call of
	 * processSentence(Tree t) where t contains leaf!!!
	 */
	protected Node addNode(final Tree leaf) {

		if (!leaf.isLeaf())
			throw new java.lang.IllegalArgumentException("Tree is not a leaf!");

		// extract word and its position and add it to the network
		final String word = leaf.toString();
		final TextPosition textPos = new TextPosition(currentSentenceIndex,
				positions.get(leaf));
		return addNode(word, textPos);
	}

	/**
	 * Assigns the integers 0 to (n-1) to the n leaves of the given tree.
	 * 
	 * @param positions
	 *            the Map where the positions are stored in
	 */
	public void assignWordPositions(final Tree tree) {

		positions = new IdentityHashMap<Tree, Integer>();
		final List<Tree> leaves = tree.getLeaves();
		for (int i = 0; i < leaves.size(); i++)
			positions.put(leaves.get(i), new Integer(i));

	}

}