package de.visone.nlpext.network;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import atomicwheel.Node;
import de.visone.ext.nlp.TextPosition;
import edu.stanford.nlp.ling.HasTag;
import edu.stanford.nlp.ling.HasWord;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.tagger.maxent.MaxentTagger;

/**
 * Creates a text network using the Word Network Analysis by Danowski, i.e.
 * sliding a window across the text and connecting all pairs of words that lie
 * within that window's size.
 * 
 * @author endele, revised by nick
 */
public class WNANetwork extends TextNetwork {

	private int windowSize;
	private final List<Node> wordQueue = new LinkedList<Node>();
	private Set<String> tagFilter;
	private final MaxentTagger tagger;
	private boolean m_coOccurrence;

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

	public WNANetwork(final MaxentTagger posTagger, final int windowSize,
			final boolean layout) {
		this(posTagger, windowSize, null, null, 3, false, layout);
	}

	public WNANetwork(final MaxentTagger posTagger, final int windowSize,
			final String[] posTags, final String[] stopWords,
			final int wordLength, final boolean useCoOccurrence,
			final boolean layout) {
		super(layout);
		tagger = posTagger;
		setWindowSize(windowSize);
		setTagFilter(posTags);
		setStopWords(stopWords);
		setMinWordLength(wordLength);
		m_coOccurrence = useCoOccurrence;
	}

	@Override
	public void setNetwork() {
		super.setNetwork();
		wordQueue.clear();
	}

	public void setCoOccurrence(final boolean coOccurrence) {
		m_coOccurrence = coOccurrence;
	}

	@Override
	protected void processSentence(final List<? extends HasWord> sentence,
			final int sentenceIndex) {
		final List<TaggedWord> taggedSent = tagger.apply(sentence);

		for (int i = 0; i < taggedSent.size(); i++) {
			final HasWord hasWord = taggedSent.get(i);
			final String word = hasWord.word();
			final String tag = ((HasTag) hasWord).tag();
			// if (tagFilter.contains(tag) && !ParserUtil.isStopword(word)) {
			if (tagFilter.contains(tag) && !isStopword(word)
					&& word.length() >= getMinWordLength()) {
				final TextPosition textPos = new TextPosition(sentenceIndex, i);
				final Node node = addNode(word, textPos);

				wordQueue.add(node);
			}
		}

		processWindow();
	}

	/**
	 * Reduces the word-queue by moving the window over it and building cliques.
	 */
	private void processWindow() {
		// generate the co-occcurrence graph
		if (m_coOccurrence) {
			while (!wordQueue.isEmpty()) {
				final Node v = wordQueue.remove(0);
				for (final Node w : wordQueue) {
					addEdge(v, w);
				}
			}
		} else {
			while (wordQueue.size() >= windowSize) {
				// remove the first element from the queue
				final Node v = wordQueue.remove(0);
				// and connect it to the (windowSize-1) next nodes in the list
				for (final Node w : wordQueue.subList(0, windowSize - 1)) {
					addEdge(v, w);
				}
			}
		}
	}

	/** */
	@Override
	protected void finalizeCreation() {
		while (!wordQueue.isEmpty()) {
			// remove the first element from the queue
			final Node v = wordQueue.remove(0);
			// and connect it to the remaining next nodes in the queue
			for (final Node w : wordQueue) {
				addEdge(v, w);
			}
		}
	}

	/** Return a description of this network. */
	@Override
	public String getDescription() {
		final String descr = "WNA-network with window size " + windowSize;
		return descr;
	}

	/**
	 * Sets the window size.
	 * 
	 * @throws IllegalArgumentException
	 *             if windowSize is less than 2
	 */
	public void setWindowSize(final int windowSize) {
		if (windowSize < 2)
			throw new IllegalArgumentException("Window size " + windowSize
					+ " makes no sense!");
		this.windowSize = windowSize;
	}

	/** Returns the current window size. */
	public int getWindowSize() {
		return windowSize;
	}

	/** defines which pos tags to include */
	public void setTagFilter(final String[] posTags) {
		if (posTags == null)
			tagFilter = getDefaultTagSet();
		else {
			final HashSet<String> filter = new HashSet<String>();
			for (final String posTag : posTags) {
				filter.add(posTag);
			}
			tagFilter = filter;
		}

	}

	/**
	 * Returns a HashSet containing all standard word-tags to allow as tags of
	 * important words.
	 */
	public static HashSet<String> getDefaultTagSet() {
		final HashSet<String> filter = new HashSet<String>();
		// words should be...
		filter.add("FW"); // foreign word
		filter.add("JJ"); // adjective
		filter.add("JJR"); // adjective, comparative
		filter.add("JJS"); // adjective, superlative
		filter.add("NN"); // noun, singular or mass
		filter.add("NNS"); // noun plural
		filter.add("NNP"); // proper noun, singular
		filter.add("NNPS"); // proper noun, plural
		filter.add("RB"); // adverb
		filter.add("RBR"); // adverb, comparative
		filter.add("RBS"); // adverb, superlative
		filter.add("VB"); // verb, base form
		filter.add("VBD"); // verb, past tense
		filter.add("VBG"); // verb, gerund/present participle
		filter.add("VBN"); // verb, past participle
		filter.add("VBP"); // verb, sing. present, non-3d
		filter.add("VBZ"); // verb, 3rd person sing. present
		return filter;
	}

}