package de.visone.nlpext.gui;

import java.awt.Frame;
import java.io.File;

import de.visone.base.Mediator;
import de.visone.gui.tabs.option.BooleanOptionItem;
import de.visone.gui.tabs.option.InputFileOptionItem;
import de.visone.gui.tabs.option.IntegerOptionItem;
import de.visone.nlpext.NLPExtension;
import de.visone.nlpext.network.CreationProcess;
import de.visone.nlpext.network.TextNetwork;
import de.visone.nlpext.network.WNANetwork;
import edu.stanford.nlp.tagger.maxent.MaxentTagger;

/**
 * Panel with option for the WNA algorithm.
 * 
 * @author unknown
 * @author matthias
 */
public class WNACard extends NLPPanel {
	private static final String TAGGER_FILE_NOT_FOUND = "please specify a valid tagger file";
	private static final String COOCCURRENCE_LABEL = "use co-occurrence";
	private static final String USE_DEFAULT_LABEL = "use default tag set";
	private static final String TAG_FILTER_LABEL = "tag file";
	private static final String WINDOW_SIZE_LABEL = "size";
	private static final String TAGGER_FILE_LABEL = "tagger file";

	private static final String DEFAULT_TAGGER_PATH = "models/wsj-0-18-left3words.tagger";

	/**
	 * default tagger filename. located in visone's working directory. that
	 * should be wherever it's installed, so the user just has to copy the file
	 * there and not worry about it any further.
	 */
	private static final File TAGGERFILE = new File(
			"left3words-wsj-0-18.tagger");

	private static final String POS_TAG_FILTER = "/de/visone/nlpext/resource/pos_tag_filter.txt";

	private MaxentTagger tagger;
	private String taggerFileName;

	private InputFileOptionItem taggerOption;
	private BooleanOptionItem cooccurence;
	private IntegerOptionItem rotate;
	private InputFileOptionItem posTagBox;
	private BooleanOptionItem defaultPOSTags;
	private BooleanOptionItem useBuiltin;

	private static final int DEFAULT_WINSIZE = 5;
	private static final int MIN_WINSIZE = 2;
	private static final int MAX_WINSIZE = 30;

	/**
	 * @param mediator
	 *            the mediator
	 */
	public WNACard(final Mediator mediator) {
		super(mediator, "word network analysis");
	}

	@Override
	public String getApplyText() {
		return "create WNA network";
	}

	// ------------ get methods ------------

	@Override
	protected void initializePanel() {
		addBasicOptions();

		addHeading("Stanford maxent tagger");
		useBuiltin = new BooleanOptionItem();
		useBuiltin.setValue(true);
		affectsOthers(useBuiltin);
		addOptionItem(useBuiltin, "use builtin");
		taggerOption = new InputFileOptionItem("tagger file", ".tagger");
		taggerOption.setValue(TAGGERFILE);
		addOptionItem(taggerOption, TAGGER_FILE_LABEL);

		addHeading("window");
		cooccurence = new BooleanOptionItem();
		affectsOthers(cooccurence);
		addOptionItem(cooccurence, COOCCURRENCE_LABEL);
		rotate = new IntegerOptionItem(DEFAULT_WINSIZE, MIN_WINSIZE,
				MAX_WINSIZE);
		addOptionItem(rotate, WINDOW_SIZE_LABEL);

		addHeading("POS tag filter");
		defaultPOSTags = new BooleanOptionItem();
		defaultPOSTags.setValue(true);
		affectsOthers(defaultPOSTags);
		addOptionItem(defaultPOSTags, USE_DEFAULT_LABEL);
		posTagBox = new InputFileOptionItem("tag filter", ".txt");
		addOptionItem(posTagBox, TAG_FILTER_LABEL);
	}

	@Override
	protected void toggleOptionItemsEnabled() {
		rotate.setEnabled(!cooccurence.getValue());
		posTagBox.setEnabled(!defaultPOSTags.getValue());
		taggerOption.setEnabled(!useBuiltin.getValue());
		super.toggleOptionItemsEnabled();
	}

	/** returns true if default pos tags shall be used. */
	public boolean useDefaultPOSTags() {
		return defaultPOSTags.getValue();
	}

	/** return window size that was set by the user. */
	public int getWindowSize() {
		return rotate.getValue();
	}

	public boolean useCoOccurrence() {
		return cooccurence.getValue();
	}

	protected void loadTagger() {
		final String newTaggerFileName = getTaggerFileName();
		// need to reload the parser if:
		// - none is loaded
		boolean mustLoadTagger = tagger == null;
		// - the builtin one is loaded, but an external one selected
		if (taggerFileName == null && newTaggerFileName != null)
			mustLoadTagger = true;
		// - an external one is loaded, but the builtin one selected
		if (taggerFileName != null && newTaggerFileName == null)
			mustLoadTagger = true;
		// - an external one is loaded, but a different external one is
		// selected
		if (taggerFileName != null && newTaggerFileName != null
				&& !newTaggerFileName.equals(taggerFileName))
			mustLoadTagger = true;

		if (mustLoadTagger)
			new TaggerLoadingProcess(getTaggerFile(newTaggerFileName));
	}

	/**
	 * returns an initialized <code>WNANetwork</code> or <code>null</code> if
	 * this was not possible.
	 */
	@Override
	public TextNetwork getTextNetwork() {
		// try to load the (new) pos tagger
		try {
			loadTagger();

			if (tagger != null) {

				// get POS tags
				final String[] posTags = extractLinesFromFile(
						posTagBox.getValue(), POS_TAG_FILTER,
						"could not read POS tag filter file."
								+ " will use default filter.",
						useDefaultPOSTags());

				// get stop words
				final String[] stopWords = extractLinesFromFile(
						stopwords.getValue(), STOP_WORDS_FILTER,
						CANNOT_LOAD_STOPWORDS, useDefaultStopWords());

				final int minWordLength = getMinWordLength();

				// instantiate wna network
				return new WNANetwork(tagger, getWindowSize(), posTags,
						stopWords, minWordLength, useCoOccurrence(),
						getLayoutMode());

			} else
				return null;

		} catch (final Exception e) {
			return null;
		}

	}

	@Override
	public void runAlgorithm() {
		final File textFile = getTextFile();
		if (textFile == null)
			return;

		final TextNetwork textNetw = getTextNetwork();
		if (textNetw == null)
			return;

		final boolean doQuickLayout = getLayoutMode();
		new CreationProcess(textFile, textNetw, doQuickLayout, mediator);
	}

	private String getTaggerFileName() {
		if (useBuiltin.getValue())
			return null;
		return taggerOption.getValue().getAbsolutePath();
	}

	private File getTaggerFile(final String filename) {
		if (filename == null)
			return null;
		try {
			if (filename.equals(""))
				throw new IllegalArgumentException(TAGGER_FILE_NOT_FOUND);
			final File taggerFile = new File(filename);
			return taggerFile;
		} catch (final Exception e) {
			error(TAGGER_FILE_NOT_FOUND);
			return null;
		}
	}

	// ------------ other methods ------------

	/** Called by the TaggerLoadingProcess when finished. */
	private void finishLoading(final Throwable throwable, final String filename) {
		if (throwable == null) {
			taggerFileName = filename;
			return;
		}

		// else: an exception occurred, throwable != null

		tagger = null;

		if (throwable instanceof OutOfMemoryError)
			error("could not load tagger -- out of memory."
					+ " please start Java with option -Xmx1g.");
		else if (throwable instanceof java.io.InterruptedIOException) {
			// Loading was interrupted by the user
		} else
			error("tagger could not be loaded.\n (" + filename + ")\n"
					+ throwable.getMessage());
	}

	// ------------ private class ------------

	/**
	 * Class for loading a pos tagger from specified file via new thread.
	 */
	private class TaggerLoadingProcess extends Thread {

		private File taggerFile = null;
		private Throwable throwable = null;
		ProgressDialog progressDialog;

		public TaggerLoadingProcess(final File taggerFile) {

			this.taggerFile = taggerFile;
			run();
		}

		@Override
		public void run() {
			try {
				// FIXME monitoring and canceling progress does not work yet
				final Frame parentWindow = mediator.getWindow();
				if (mediator.getWindow() != null) {
					progressDialog = new ProgressDialog(parentWindow,
							"Loading POS Tagger...",
							ProgressDialog.STYLE_INDETERMINATE);
					progressDialog.setVisible(true);
					progressDialog.indeterminateProgressBar
							.paintImmediately(progressDialog.indeterminateProgressBar
									.getVisibleRect());
				}

				if (taggerFile == null)
					tagger = new MaxentTagger(NLPExtension.class.getResource(
							DEFAULT_TAGGER_PATH).toExternalForm());
				else
					tagger = new MaxentTagger(taggerFile.getAbsolutePath());
			} catch (final Throwable t) {
				throwable = t;
			} finally {
				if (taggerFile != null)
					finishLoading(throwable, taggerFile.getAbsolutePath());
				else
					finishLoading(throwable, null);
			}

			// close the progress dialog when everything is done
			if (progressDialog != null)
				progressDialog.setVisible(false);
		}
	}
}
