package de.visone.nlpext.network;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import edu.stanford.nlp.ling.Word;
import edu.stanford.nlp.process.Morphology;
import edu.stanford.nlp.trees.Tree;

/**
 * Uninstanciatable utility class that provides methods for stemming a word,
 * checking if a word is a stop word, replacing synonyms according to the
 * synonyms list contained in the resources, parsing and handling of ParseTrees.
 * 
 * @author <a href="mailto:endele@inf.uni-konstanz.de">Simon Endele</a>
 */
public class ParserUtil {

	private static final String STOP_WORDS_PATH = "/de/visone/nlpext/resource/stop_words.txt";
	private static HashSet<String> stopWords = new HashSet<String>();

	private static final String SYNONYMS_PATH = "/de/visone/nlpext/resource/synonyms.txt";
	private static final LinkedHashMap<String, String> synonyms = new LinkedHashMap<String, String>();

	private static Morphology morphology = new Morphology();

	static {
		// read in stop-word-list from STOP_WORDS_PATH
		try {
			final InputStream in = ParserUtil.class
					.getResource(STOP_WORDS_PATH).openStream();
			final BufferedReader reader = new BufferedReader(
					new InputStreamReader(in));
			while (reader.ready()) {
				stopWords.add(reader.readLine().trim());
			}
			reader.close();
		} catch (final IOException e) {
			System.out.println("ParserUtil: Could not read " + STOP_WORDS_PATH
					+ "!");
		}
		// read in synonyms from SYNONYMS_PATH
		try {
			final InputStream in = ParserUtil.class.getResource(SYNONYMS_PATH)
					.openStream();
			final BufferedReader reader = new BufferedReader(
					new InputStreamReader(in));
			while (reader.ready()) {
				String line = reader.readLine();
				if (line == null)
					break;
				if (line.indexOf("#") != -1)
					line = line.substring(0, line.indexOf("#")).trim();
				if (line == "" || line.startsWith("#"))
					continue;
				final String[] entries = line.split("/");
				if (entries.length < 2)
					continue;
				synonyms.put(entries[0].trim(), entries[1].trim());
			}
			reader.close();
			/*
			 * for (String key : synonyms.keySet()) {
			 * System.out.println("\tParserUtil: synonym: "
			 * +key+" -> "+synonyms.get(key)); }
			 */
		} catch (final IOException e) {
			System.out.println("ParserUtil: Could not read " + STOP_WORDS_PATH
					+ "!");
		}
	}

	private ParserUtil() {
	}

	/** Stems a given word. */
	public static String stem(final String word) {
		return word.contains("_") ? word.toLowerCase() : morphology.stem(
				new Word(word)).word();
	}

	/** Checks if a word is a stop-word. */
	public static boolean isStopword(final String word) {
		// System.out.println("-" + word + "-" + stopWords.contains(word));
		return stopWords.contains(word);
	}

	/**
	 * Replaces all keys in the synonyms-LinkedHashMap by their values in the
	 * order they appear in the synonyms file contained in the resources.
	 */
	public static String replaceSynonyms(String text) {
		for (final String key : synonyms.keySet()) {
			text = text.replaceAll(key, synonyms.get(key));
			// text = text.replaceAll(Pattern.quote(key),
			// Pattern.quote(synonyms.get(key)));
		}
		return text;
	}

	/**
	 * 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 static void assignWordPositions(final Tree tree,
			final Map<Tree, Integer> positions) {
		final List<Tree> leaves = tree.getLeaves();
		for (int i = 0; i < leaves.size(); i++) {
			positions.put(leaves.get(i), new Integer(i));
			System.out.println("*** size:" + positions.size());
		}
	}

	/**
	 * Returns the nodes of all nominal-phrases (NPs) on the hightes level.
	 * 
	 * @param phrases
	 *            the list where the phrases are stored in
	 */
	public static List<Tree> getTopNPs(final Tree tree, final List<Tree> phrases) {
		if (tree != null) {
			if (tree.label().value().equals("NP")) {
				phrases.add(tree);
			} else {
				for (final Tree child : tree.children()) {
					getTopNPs(child, phrases);
				}
			}
		}
		return phrases;
	}

	/**
	 * Returns the nodes of all nominal-phrases (NPs) on the lowest level.
	 * 
	 * @param phrases
	 *            the list where the phrases are stored in
	 */
	// public static boolean getBottomNPs(Tree tree, List<Tree> phrases) {
	// if (tree == null) {
	// return false;
	// } else {
	// boolean existsLowerNP = false;
	// for (Tree child : tree.children()) {
	// existsLowerNP |= getBottomNPs(child, phrases);
	// }
	// boolean isThisNP = tree.label().value().equals("NP");
	// if (isThisNP && !existsLowerNP) {
	// phrases.add(tree);
	// }
	// return isThisNP || existsLowerNP;
	// }
	// }
	public static boolean getBottomNPs(final Tree tree, final List<Tree> phrases) {
		if (tree == null) {
			return false;
		} else {
			boolean existsLowerNP = false;
			for (final Tree child : tree.children()) {
				existsLowerNP |= getBottomNPs(child, phrases);
			}
			final boolean isThisNP = isRightKindOfPhrase(tree.label().value());
			if (isThisNP && !existsLowerNP) {
				phrases.add(tree);
			}
			return isThisNP || existsLowerNP;
		}
	}

	public static boolean isRightKindOfPhrase(final String label) {
		if (label.equals("NP") || label.equals("ADJP") || label.equals("VP"))
			return true;
		else
			return false;
	}

	/**
	 * Returns all leaves of the given tree whose parents' label texts are
	 * contained in the filter.
	 * 
	 * @param leaves
	 *            list to which the leaves are added
	 */
	public static List<Tree> getLeaves(final Tree tree,
			final List<Tree> leaves, final Set<String> filter) {
		if (tree != null) {
			final Tree[] children = tree.children();
			for (final Tree child : children) {
				if (child.isLeaf() && filter.contains(tree.label().value())) {
					leaves.add(child);
				} else {
					getLeaves(child, leaves, filter);
				}
			}
		}
		return leaves;
	}

	/**
	 * Removes all double entries of an Object from the list. Only the last
	 * occurance will remain.
	 * 
	 * @param list
	 *            should be backed by a random access data store and must
	 *            support the remove(int) operation
	 */
	public static <E> void makeUnique(final List<E> list) {
		for (int i = 0; i < list.size(); i++) {
			final E e = list.get(i);
			if (list.lastIndexOf(e) > i) {
				list.remove(i);
				i--;
			}
		}
	}

	// // oder anders: von hinten durchgehen, Eintraege in HashMap, removen wenn
	// schon drin

	/** Test method. */
	public static void main(final String[] args) {
		System.out.println(replaceSynonyms(args[0]));

		/*
		 * for (String arg : args) {
		 * System.out.println("isStopword("+arg+"): "+isStopword(arg)); }
		 */

		/*
		 * Vector<String> list = new Vector<String>(Arrays.asList(args));
		 * //List<String> list = Arrays.asList(args); makeUnique(list); for
		 * (String s : list) { System.out.println(s); }
		 */
	}
}