/*
 *  JOrtho
 *
 *  Copyright (C) 2005-2008 by i-net software
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as 
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version. 
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 *  USA.
 *  
 * Created on 25.02.2008
 */
package com.inet.jortho;

import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Locale;

import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;

/**
 * Is used from CheckerMenu and CheckerPopup to handle the user events.
 * @author Volker Berlin
 */
public class CheckerListener implements PopupMenuListener, LanguageChangeListener {
	private Dictionary dictionary;
	private Locale locale;
	private final JComponent menu;
	private final SpellCheckerOptions options;

	CheckerListener(final JComponent menu, final SpellCheckerOptions options) {
		this.menu = menu;
		this.options = options == null ? SpellChecker.getOptions() : options;
		SpellChecker.addLanguageChangeLister(this);
		dictionary = SpellChecker.getCurrentDictionary();
		locale = SpellChecker.getCurrentLocale();
	}

	public void languageChanged(final LanguageChangeEvent ev) {
		dictionary = SpellChecker.getCurrentDictionary();
		locale = SpellChecker.getCurrentLocale();
	}

	public void popupMenuCanceled(final PopupMenuEvent e) {
		/* empty */
	}

	public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
		/* empty */
	}

	public void popupMenuWillBecomeVisible(final PopupMenuEvent ev) {
		if (dictionary == null || locale == null) {
			menu.setEnabled(false);
			return;
		}
		final JPopupMenu popup = (JPopupMenu) ev.getSource();
		final Component invoker = popup.getInvoker();
		if (invoker instanceof JTextComponent) {
			final JTextComponent jText = (JTextComponent) invoker;
			if (!jText.isEditable()) {
				// Suggestions only for editable text components
				menu.setEnabled(false);
				return;
			}
			final Caret caret = jText.getCaret();
			int offs = Math.min(caret.getDot(), caret.getMark());
			final Point p = jText.getMousePosition();
			if (p != null) {
				// use position from mouse click and not from editor cursor position 
				offs = jText.viewToModel(p);
			}
			try {
				final Document doc = jText.getDocument();
				if (offs > 0 && (offs >= doc.getLength() || Character.isWhitespace(doc.getText(offs, 1).charAt(0)))) {
					// if the next character is a white space then use the word on the left site
					offs--;
				}
				if (offs < 0) {
					// occur if there nothing under the mouse pointer
					menu.setEnabled(false);
					return;
				}
				// get the word from current position
				final int begOffs = Utilities.getWordStart(jText, offs);
				final int endOffs = Utilities.getWordEnd(jText, offs);
				final String word = jText.getText(begOffs, endOffs - begOffs);
				//find the first invalid word from current position
				final Tokenizer tokenizer = new Tokenizer(jText, dictionary, locale, offs, options);
				String invalidWord;
				do {
					invalidWord = tokenizer.nextInvalidWord();
				} while (tokenizer.getWordOffset() < begOffs);
				menu.removeAll();
				if (!word.equals(invalidWord)) {
					// the current word is not invalid
					menu.setEnabled(false);
					return;
				}
				if (dictionary == null) {
					// without dictionary it is disabled
					menu.setEnabled(false);
					return;
				}
				final List<Suggestion> list = dictionary.searchSuggestions(word);
				//Disable then menu item if there are no suggestions
				menu.setEnabled(list.size() > 0);
				final boolean needCapitalization = tokenizer.isFirstWordInSentence() && Utils.isFirstCapitalized(word);
				for (int i = 0; i < list.size() && i < options.getSuggestionsLimitMenu(); i++) {
					final Suggestion sugestion = list.get(i);
					String sugestionWord = sugestion.getWord();
					if (needCapitalization) {
						sugestionWord = Utils.getCapitalized(sugestionWord);
					}
					final JMenuItem item = new JMenuItem(sugestionWord);
					menu.add(item);
					final String newWord = sugestionWord;
					item.addActionListener(new ActionListener() {
						public void actionPerformed(final ActionEvent e) {
							jText.setSelectionStart(begOffs);
							jText.setSelectionEnd(endOffs);
							jText.replaceSelection(newWord);
						}
					});
				}
				final UserDictionaryProvider provider = SpellChecker.getUserDictionaryProvider();
				if (provider == null) {
					return;
				}
				final JMenuItem addToDic = new JMenuItem(Utils.getResource("addToDictionary"));
				addToDic.addActionListener(new ActionListener() {
					public void actionPerformed(final ActionEvent e) {
						final UserDictionaryProvider provider = SpellChecker.getUserDictionaryProvider();
						if (provider != null) {
							provider.addWord(word);
						}
						dictionary.add(word);
						dictionary.trimToSize();
						AutoSpellChecker.refresh(jText);
					}
				});
				if (list.size() > 0) {
					if (menu instanceof JMenu) {
						((JMenu) menu).addSeparator();
					}
					else if (menu instanceof JPopupMenu) {
						((JPopupMenu) menu).addSeparator();
					}
				}
				menu.add(addToDic);
				menu.setEnabled(true);
			}
			catch (final BadLocationException ex) {
				ex.printStackTrace();
			}
		}
	}
}
