package de.visone.nlpext.gui;

import java.io.File;
import java.io.ObjectInputStream;
import java.util.zip.GZIPInputStream;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import de.visone.base.Mediator;
import de.visone.gui.window.VisoneWindow;
import de.visone.nlpext.NLPExtension;
import edu.stanford.nlp.parser.lexparser.LexicalizedParser;

/**
 * Class for loading a parser from specified file via new thread.
 * 
 * @author unknown
 * @author matthias
 */
public final class ParserLoadingProcess {
	private static final Logger logger = Logger
			.getLogger(ParserLoadingProcess.class);

	public static final String PARSER_FILE_NOT_FOUND = "please specify a valid parser file";

	private static final String DEFAULT_PARSER_PATH = "models/englishPCFG.ser.gz";

	/**
	 * 
	 * @param listener
	 *            the {@link ParserLoaderListener} that is informed when loading
	 *            has succeeded or failed
	 * @param filename
	 *            the parser file to load
	 * @param mediator
	 *            the {@link Mediator}
	 */
	public static void loadParser(final ParserLoaderListener listener,
			final String filename, final Mediator mediator) {
		final VisoneWindow parentWindow = mediator.getWindow();

		final String parserPath;
		if (filename != null) {
			final File parserFile = getParserFile(filename, parentWindow);
			if (parserFile == null) {
				listener.failLoading(filename);
				return;
			}
			parserPath = parserFile.getAbsolutePath();

			logger.debug("loading parser file: " + parserPath);
		} else {
			parserPath = null;
			logger.debug("loading builtin parser");
		}

		final ProgressDialog progressDialog;
		// FIXME monitoring and canceling progress does not work yet
		if (parentWindow != null) {
			progressDialog = new ProgressDialog(parentWindow,
					"loading parser...", ProgressDialog.STYLE_INDETERMINATE);
			progressDialog.setVisible(true);
			progressDialog.indeterminateProgressBar
					.paintImmediately(progressDialog.indeterminateProgressBar
							.getVisibleRect());
		} else
			progressDialog = null;

		try {
			logger.debug("create parser");
			final LexicalizedParser parser;
			if (parserPath == null) {
				final ObjectInputStream inputStream = new ObjectInputStream(
						new GZIPInputStream(NLPExtension.class
								.getResourceAsStream(DEFAULT_PARSER_PATH)));
				parser = LexicalizedParser.loadModel(inputStream);
				inputStream.close();
			} else
				parser = LexicalizedParser.loadModel(parserPath);
			listener.finishLoading(parser, parserPath);
			logger.debug("parser created");
		} catch (final OutOfMemoryError e) {
			logger.error("out of memory", e);
			error(parentWindow, "could not load parser -- out of memory."
					+ " please start Java with option -Xmx1g.");
			listener.failLoading(parserPath);
			// } catch (java.io.InterruptedIOException e) {
			// logger.debug("loading interrupted by user", e);
			// listener.failLoading(e, parserPath);
		} catch (final Throwable t) {
			logger.error("loading error: " + t.getMessage(), t);
			error(parentWindow, "parser could not be loaded.\n (" + filename
					+ ")\n" + t.getMessage());
			listener.failLoading(parserPath);
		}

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

	/**
	 * This is the listener that gets informed when the parser has finished (or
	 * failed) loading.
	 * 
	 * @author matthias
	 */
	public interface ParserLoaderListener {
		/**
		 * Called when the parser was loaded successfully.
		 * 
		 * @param parser
		 *            the parser that was loaded
		 * @param absolutePath
		 *            the absolute path to the parser file
		 */
		void finishLoading(LexicalizedParser parser, String absolutePath);

		/**
		 * Called when the parser failed to load.
		 * 
		 * @param absolutePath
		 *            the absolute path to the parser file
		 * */
		void failLoading(String absolutePath);
	}

	private static File getParserFile(final String filename, final JFrame parent) {
		try {
			if (filename == null || filename.equals(""))
				throw new IllegalArgumentException(PARSER_FILE_NOT_FOUND);
			final File parserFile = new File(filename);
			return parserFile;
		} catch (final Exception e) {
			logger.error("parser file missing " + e.getMessage(), e);
			error(parent, PARSER_FILE_NOT_FOUND);
			return null;
		}
	}

	private static void error(final JFrame parent, final String message) {
		JOptionPane.showMessageDialog(parent, message, "error loading parser",
				JOptionPane.ERROR_MESSAGE);
	}

	private ParserLoadingProcess() {
	}
}
