/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool
          with fuzzy matching, translation memory, keyword search,
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2009 Didier Briel
               2010 Wildrich Fourie
               2011 Alex Buloichik, Didier Briel
               2015 Aaron Madlon-Kay
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT 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 3 of the License, or
 (at your option) any later version.

 OmegaT 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, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.gui.editor;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;

import org.omegat.core.Core;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.spellchecker.SpellCheckerMarker;
import org.omegat.tokenizer.ITokenizer.StemmingMode;
import org.omegat.util.Log;
import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;
import org.omegat.util.TagUtil;
import org.omegat.util.TagUtil.Tag;
import org.omegat.util.Token;
import org.omegat.util.gui.UIThreadsUtil;

/**
 * Some standard editor popups.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Wildrich Fourie
 * @author Didier Briel
 * @author Aaron Madlon-Kay
 */
public class EditorPopups {
    public static void init(EditorController ec) {
        ec.registerPopupMenuConstructors(100, new SpellCheckerPopup(ec));
        ec.registerPopupMenuConstructors(200, new GoToSegmentPopup(ec));
        ec.registerPopupMenuConstructors(400, new DefaultPopup());
        ec.registerPopupMenuConstructors(500, new DuplicateSegmentsPopup(ec));
        ec.registerPopupMenuConstructors(600, new EmptyNoneTranslationPopup(ec));
        ec.registerPopupMenuConstructors(700, new InsertTagsPopup(ec));
        ec.registerPopupMenuConstructors(700, new InsertBidiPopup(ec));
    }

    /**
     * create the spell checker popup menu - suggestions for a wrong word, add
     * and ignore. Works only for the active segment, for the translation
     * 
     * @param point
     *            : where should the popup be shown
     */
    public static class SpellCheckerPopup implements IPopupMenuConstructor {
        protected final EditorController ec;

        public SpellCheckerPopup(EditorController ec) {
            this.ec = ec;
        }

        public void addItems(JPopupMenu menu, final JTextComponent comp, int mousepos,
                boolean isInActiveEntry, boolean isInActiveTranslation, SegmentBuilder sb) {
            if (!ec.getSettings().isAutoSpellChecking()) {
                // spellchecker disabled
                return;
            }
            if (!isInActiveTranslation) {
                // there is no need to display suggestions
                return;
            }

            // Use the project's target tokenizer to determine the word that was right-clicked.
            // EditorUtils.getWordEnd() and getWordStart() use Java's built-in BreakIterator
            // under the hood, which leads to inconsistent results when compared to other spell-
            // checking functionality in OmegaT.
            String translation = ec.getCurrentTranslation();
            Token tok = null;
            int relOffset = ec.getPositionInEntryTranslation(mousepos);
            for (Token t : Core.getProject().getTargetTokenizer().tokenizeWords(translation, StemmingMode.NONE)) {
                if (t.getOffset() <= relOffset && relOffset < t.getOffset() + t.getLength()) {
                    tok = t;
                    break;
                }
            }
            
            if (tok == null) {
                return;
            }
            
            final String word = tok.getTextFromString(translation);
            // The wordStart must be the absolute offset in the Editor document.
            final int wordStart = mousepos - relOffset + tok.getOffset();
            final int wordLength = tok.getLength();
            final AbstractDocument xlDoc = (AbstractDocument) comp.getDocument();

            if (!Core.getSpellChecker().isCorrect(word)) {
                // get the suggestions and create a menu
                List<String> suggestions = Core.getSpellChecker().suggest(word);

                // the suggestions
                for (final String replacement : suggestions) {
                    JMenuItem item = menu.add(replacement);
                    item.addActionListener(new ActionListener() {
                        // the action: replace the word with the selected
                        // suggestion
                        public void actionPerformed(ActionEvent e) {
                            try {
                                int pos = comp.getCaretPosition();
                                xlDoc.replace(wordStart, wordLength, replacement, null);
                                comp.setCaretPosition(pos);
                            } catch (BadLocationException exc) {
                                Log.log(exc);
                            }
                        }
                    });
                }

                // what if no action is done?
                if (suggestions.isEmpty()) {
                    JMenuItem item = menu.add(OStrings.getString("SC_NO_SUGGESTIONS"));
                    item.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                            // just hide the menu
                        }
                    });
                }

                menu.addSeparator();

                // let us ignore it
                JMenuItem item = menu.add(OStrings.getString("SC_IGNORE_ALL"));
                item.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        addIgnoreWord(word, wordStart, false);
                    }
                });

                // or add it to the dictionary
                item = menu.add(OStrings.getString("SC_ADD_TO_DICTIONARY"));
                item.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        addIgnoreWord(word, wordStart, true);
                    }
                });
                
                menu.addSeparator();

            }
        }

        /**
         * add a new word to the spell checker or ignore a word
         * 
         * @param word
         *            : the word in question
         * @param offset
         *            : the offset of the word in the editor
         * @param add
         *            : true for add, false for ignore
         */
        protected void addIgnoreWord(final String word, final int offset, final boolean add) {
            UIThreadsUtil.mustBeSwingThread();

            if (add) {
                Core.getSpellChecker().learnWord(word);
            } else {
                Core.getSpellChecker().ignoreWord(word);
            }

            ec.remarkOneMarker(SpellCheckerMarker.class.getName());
        }
    }

    public static class DefaultPopup implements IPopupMenuConstructor {
        /**
         * Creates the Cut, Copy and Paste menu items
         */
        public void addItems(JPopupMenu menu, final JTextComponent comp, int mousepos,
                boolean isInActiveEntry, boolean isInActiveTranslation, SegmentBuilder sb) {
            final String selText = comp.getSelectedText();
            final Clipboard omClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            Transferable contents = omClipboard.getContents(this);

            boolean cutEnabled = false;
            boolean copyEnabled = false;
            boolean pasteEnabled = false;

            // Calc enabled/disabled
            if (selText != null && comp.getSelectionStart() <= mousepos && mousepos <= comp.getSelectionEnd()) {
                // only on selected text
                if (isInActiveTranslation) {
                    // cut only in editable zone
                    cutEnabled = true;
                }
                // copy in any place
                copyEnabled = true;
            }
            if (contents != null && isInActiveTranslation) {
                pasteEnabled = true;
            }

            // Cut
            JMenuItem cutContextItem = menu.add(OStrings.getString("CCP_CUT"));
            if (cutEnabled) {
                cutContextItem.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        comp.cut();
                    }
                });
            } else {
                cutContextItem.setEnabled(false);
            }

            // Copy
            JMenuItem copyContextItem = menu.add(OStrings.getString("CCP_COPY"));
            if (copyEnabled) {
                copyContextItem.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        comp.copy();
                    }
                });
            } else {
                copyContextItem.setEnabled(false);
            }

            // Paste
            JMenuItem pasteContextItem = menu.add(OStrings.getString("CCP_PASTE"));
            if (pasteEnabled) {
                pasteContextItem.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        comp.paste();
                    }
                });
            } else {
                pasteContextItem.setEnabled(false);
            }

            menu.addSeparator();
            
			// Add glossary entry
			JMenuItem item = menu.add(OStrings.getString("GUI_GLOSSARYWINDOW_addentry"));
            item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                Core.getGlossary().showCreateGlossaryEntryDialog();
                 }
            });

            menu.addSeparator();

        }
    }

    public static class GoToSegmentPopup implements IPopupMenuConstructor {
        protected final EditorController ec;

        public GoToSegmentPopup(EditorController ec) {
            this.ec = ec;
        }

        /**
         * creates a popup menu for inactive segments - with an item allowing to
         * go to the given segment.
         */
        public void addItems(JPopupMenu menu, final JTextComponent comp, final int mousepos,
                boolean isInActiveEntry, boolean isInActiveTranslation, SegmentBuilder sb) {
            if (isInActiveEntry) {
                return;
            }

            JMenuItem item = menu.add(OStrings.getString("MW_PROMPT_SEG_NR_TITLE"));
            item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    comp.setCaretPosition(mousepos);
                    ec.goToSegmentAtLocation(comp.getCaretPosition());
                }
            });
            menu.addSeparator();
        }
    }
    
    public static class DuplicateSegmentsPopup implements IPopupMenuConstructor {
        protected final EditorController ec;
        private static int MAX_ITEMS_PER_PAGE = 10;
        
        public DuplicateSegmentsPopup(EditorController ec) {
            this.ec = ec;
        }
        
        @Override
        public void addItems(JPopupMenu menu, JTextComponent comp,
                int mousepos, boolean isInActiveEntry,
                boolean isInActiveTranslation, SegmentBuilder sb) {
            if (!isInActiveEntry) {
                return;
            }
            SourceTextEntry ste = ec.getCurrentEntry();
            List<SourceTextEntry> dups = ste.getDuplicates();
            if (dups.isEmpty()) {
                return;
            }
            JMenuItem header = menu.add(StringUtil.format(OStrings.getString("MW_GO_TO_DUPLICATE_HEADER"), dups.size()));
            header.setEnabled(false);
            JMenu submenu = null;
            for (int i = 0; i < dups.size(); i++) {
                final int entryNum = dups.get(i).entryNum();
                // Put the first n items in the main popup, but put every subsequent batch of n in
                // nested submenus to prevent any single menu from getting too tall.
                if (i > 0 && i % MAX_ITEMS_PER_PAGE == 0) {
                    JMenu newSubmenu = new JMenu(OStrings.getString("MW_MORE_SUBMENU"));
                    if (submenu == null) {
                        menu.add(newSubmenu);
                    } else {
                        submenu.add(newSubmenu);
                    }
                    submenu = newSubmenu;
                }
                String label = StringUtil.format(OStrings.getString("MW_GO_TO_DUPLICATE_ITEM"), entryNum);
                JMenuItem item = submenu == null ? menu.add(label) : submenu.add(label);
                item.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        ec.gotoEntry(entryNum);
                    }
                });
            }
            menu.addSeparator();
        }
    }

    public static class EmptyNoneTranslationPopup implements IPopupMenuConstructor {
        protected final EditorController ec;

        public EmptyNoneTranslationPopup(EditorController ec) {
            this.ec = ec;
        }

        /**
         * creates a popup menu to remove translation or set empty translation
         */
        public void addItems(JPopupMenu menu, final JTextComponent comp, final int mousepos,
                boolean isInActiveEntry, boolean isInActiveTranslation, SegmentBuilder sb) {
            if (!isInActiveEntry) {
                return;
            }

            menu.add(OStrings.getString("TRANS_POP_EMPTY_TRANSLATION")).addActionListener(
                    new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                            ec.registerEmptyTranslation();
                        }
                    });
            menu.add(OStrings.getString("TRANS_POP_REMOVE_TRANSLATION")).addActionListener(
                    new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                            ec.registerUntranslated();
                        }
                    });
            menu.add(OStrings.getString("TRANS_POP_IDENTICAL_TRANSLATION")).addActionListener(
                    new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                            ec.registerIdenticalTranslation();
                        }
                    });
            menu.addSeparator();
        }
    }

    public static class InsertTagsPopup implements IPopupMenuConstructor {
        protected final EditorController ec;

        public InsertTagsPopup(EditorController ec) {
            this.ec = ec;
        }

        public void addItems(JPopupMenu menu, final JTextComponent comp, final int mousepos, boolean isInActiveEntry,
                boolean isInActiveTranslation, SegmentBuilder sb) {
            if (!isInActiveTranslation) {
                return;
            }

            for (final Tag tag : TagUtil.getAllTagsMissingFromTarget()) {
                JMenuItem item = menu.add(StringUtil.format(OStrings.getString("TF_MENU_EDIT_TAG_INSERT_N"), tag.tag));
                item.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        Core.getEditor().insertTag(tag.tag);
                    }
                });
            }
            menu.addSeparator();
        }
    }

    public static class InsertBidiPopup implements IPopupMenuConstructor {
        protected final EditorController ec;
        protected String[] names = new String[] { "TF_MENU_EDIT_INSERT_CHARS_LRM",
                "TF_MENU_EDIT_INSERT_CHARS_RLM", "TF_MENU_EDIT_INSERT_CHARS_LRE",
                "TF_MENU_EDIT_INSERT_CHARS_RLE", "TF_MENU_EDIT_INSERT_CHARS_PDF" };
        protected String[] inserts = new String[] { "\u200E", "\u200F", "\u202A", "\u202B", "\u202C" };

        public InsertBidiPopup(EditorController ec) {
            this.ec = ec;
        }

        public void addItems(JPopupMenu menu, final JTextComponent comp, final int mousepos,
                boolean isInActiveEntry, boolean isInActiveTranslation, SegmentBuilder sb) {
            if (!isInActiveTranslation) {
                return;
            }
            JMenu submenu = new JMenu(OStrings.getString("TF_MENU_EDIT_INSERT_CHARS"));
            for (int i = 0; i < names.length; i++) {
                JMenuItem item = new JMenuItem(OStrings.getString(names[i]));
                final String insertText = inserts[i];
                item.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Core.getEditor().insertText(insertText);
                    }
                });
                submenu.add(item);
            }
            menu.add(submenu);
        }
    }
}
