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

 Copyright (C) 2000-2006 Keith Godfrey and Maxym Mykhalchuk
               2008 Alex Buloichik
               2009 Wildrich Fourie, Didier Briel, Alex Buloichik
               2012 Thomas Cordonnier
               2013 Aaron Madlon-Kay, Alex Buloichik
               2015 Didier Briel, Aaron Madlon-Kay, Thomas Cordonnier
               2016-2020 Thomas Cordonnier
               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.glossary;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import org.omegat.core.Core;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.search.TextExpression;
import org.omegat.core.glossaries.IGlossary;
import org.omegat.core.glossaries.GlossaryEntryStore;
import org.omegat.gui.common.EntryInfoSearchThread;
import org.omegat.gui.search.SearchModeBox;
import org.omegat.tokenizer.DefaultTokenizer;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.tokenizer.ITokenizer.StemmingMode;
import org.omegat.util.Preferences;
import org.omegat.util.StringUtil;
import org.omegat.util.Token;

/**
 * Class for find glossary entries for current entry in editor.
 * 
 * This process looks up the source string entries, and find matched glossary
 * entries.
 * <p>
 * Test cases wheter a glossary entry matches a string entry text:
 * <ul>
 * <li>"Edit" vs "Editing" - doesn't match
 * <li>"Old Line" vs "Hold Line" - doesn't match
 * <li>"Some Text" vs "There was some text there" - OK!
 * <li>"Edit" vs "Editing the edit" - matches OK!
 * <li>"Edit" vs "Edit" - matches OK!
 * </ul>
 * 
 * @author Keith Godfrey
 * @author Maxym Mykhalchuk
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Wildrich Fourie
 * @author Didier Briel
 * @author Aaron Madlon-Kay
 * @author Thomas Cordonnier
 */
public class FindGlossaryThread extends EntryInfoSearchThread<List<GlossaryEntryView>> {

    private final String src;

    private final GlossaryManager manager;

    public FindGlossaryThread(final GlossaryTextArea pane, final SourceTextEntry newEntry,
            final GlossaryManager manager) {
        super(pane, newEntry);
        if (Preferences.isPreferenceDefault(Preferences.GLOSSARY_REMOVE_TAGS, false)) 
            src = newEntry.getSrcText().replaceAll("</?\\w+/?>", "");
        else
            src = newEntry.getSrcText();			
        this.manager = manager;
    }

    @Override
    protected List<GlossaryEntryView> search() {
        
        SearchModeBox dummyBox = new SearchModeBox (0, ITokenizer.StemmingMode.GLOSSARY, null);
        dummyBox.loadPreferences (Preferences.TRANSTIPS + "_");
        
        List<GlossaryEntryStore> entries = manager.getGlossaryEntries(src);
        if (entries == null) {
            return Collections.emptyList();
        }
        
        List<GlossaryEntryStore> result = new ArrayList<GlossaryEntryStore>();
        
        for (GlossaryEntryStore glosEntry : entries) {
            checkEntryChanged();
            
            TextExpression expr = dummyBox.buildExpression(glosEntry.getSrcText(), false, true);				
            if (expr.matchesString (src)) {
                result.add(glosEntry);
                continue;
            }
            
            if (!Core.getProject().getProjectProperties().getSourceLanguage().isSpaceDelimited()
                    && StringUtil.isCJK(glosEntry.getSrcText()) && src.contains(glosEntry.getSrcText())) {
                // This is a CJK word and our source language is not space-delimited, so include if
                // word appears anywhere in source string.
                result.add(glosEntry);
            }
        }

        // After the matched entries have been tokenized and listed.
        // We reorder entries: 1) by priority, 2) by length, 3) by alphabet
        // Then remove the duplicates and combine the synonyms.
        Collections.sort(result, GlossarySortCriteria.buildFullComparator(Preferences.getPreferenceDefault(Preferences.GLOSSARY_PANE_SORT_ORDER, "APL;apl")));
        return filterGlossary(result);
    }
    
    static List<GlossaryEntryView> filterGlossary(List<GlossaryEntryStore> result) {
        // First check that entries exist in the list.
        if (result.isEmpty()) {
            return Collections.emptyList();
        }

        // The default replace entry
        final GlossaryEntryStore replaceEntry = new GlossaryEntryStore("", "", "", null);

        // ... Remove the duplicates from the list
        // ..............................
        boolean removedDuplicate = false;
        SearchModeBox dummyBox = null;
		if (Preferences.isPreferenceDefault(Preferences.GLOSSARY_REMOVE_SUBWORDS, false)) {
			dummyBox = new SearchModeBox (0, ITokenizer.StemmingMode.GLOSSARY, null);
			dummyBox.loadPreferences (Preferences.TRANSTIPS + "_");
		}
        for (int i = 0; i < result.size(); i++) {
            GlossaryEntryStore nowEntry = result.get(i);

            if (nowEntry.getSrcText().equals(""))
                continue;

            for (int j = i + 1; j < result.size(); j++) {
                GlossaryEntryStore thenEntry = result.get(j);

                if (thenEntry.getSrcText().equals(""))
                    continue;

                // If the Entries are exactely the same, insert a blank entry.
                if (nowEntry.equals(thenEntry))  {
                    result.set(j, replaceEntry);
                    removedDuplicate = true;
                }
						
				if (Preferences.isPreferenceDefault(Preferences.GLOSSARY_REMOVE_SUBWORDS, false)) {
					// remove thenEntry if it is a subset of nowEntry
					TextExpression expr = dummyBox.buildExpression(thenEntry.getSrcText(), false, true);
					if (expr.searchString (nowEntry.getSrcText()) != null) {
                        result.set(j, replaceEntry);
                        removedDuplicate = true;
					}
				}
            }
        }
        // ......................................................................

        // -- Remove the blank entries from the list
        // ----------------------------
        if (removedDuplicate) {
            Iterator<GlossaryEntryStore> myIter = result.iterator();
            List<GlossaryEntryStore> newList = new LinkedList<GlossaryEntryStore>();

            while (myIter.hasNext()) {
                GlossaryEntryStore checkEntry = myIter.next();
                if (checkEntry.equals(replaceEntry))
                    myIter.remove();
                else
                    newList.add(checkEntry);
            }

            result = newList;
        }
        
        // ----------------------------------------------------------------------

        // ~~ Group items with same scrTxt
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        List<GlossaryEntryView> returnList = new LinkedList<GlossaryEntryView>();

        if (! Preferences.isPreferenceDefault(Preferences.GLOSSARY_PANE_DO_MERGE, true)) {
	  returnList.addAll(result); return returnList;
      }
        
        for (int i = 0; i < result.size(); i++) {
            List<GlossaryEntryStore> srcList = new LinkedList<GlossaryEntryStore>();
            GlossaryEntryStore nowEntry = result.get(i);

            if (nowEntry.getSrcText().equals(""))
                continue;

            srcList.add(nowEntry);

            for (int j = i + 1; j < result.size(); j++) {
                GlossaryEntryStore thenEntry = result.get(j);

                // Double check, needed?
                if (thenEntry.getSrcText().equals(""))
                    continue;

                if (nowEntry.getSrcText().equals(thenEntry.getSrcText())) {
                    srcList.add(thenEntry);
                    result.set(j, replaceEntry);
                }
            }

            // == Sort items with same locTxt
            // ==============================
            List<GlossaryEntryStore> sortList = new LinkedList<GlossaryEntryStore>();
            if (srcList.size() > 1) {
                for (int k = 0; k < srcList.size(); k++) {
                    GlossaryEntryStore srcNow = srcList.get(k);

                    if (srcNow.getSrcText().equals(""))
                        continue;

                    sortList.add(srcNow);

                    for (int l = k + 1; l < srcList.size(); l++) {
                        GlossaryEntryStore srcThen = srcList.get(l);

                        if (srcThen.getSrcText().equals(""))
                            continue;

                        if (srcNow.getLocText().equals(srcThen.getLocText())) {
                            sortList.add(srcThen);
                            srcList.set(l, replaceEntry);
                        }
                    }
                }
            } else {
                sortList = srcList;
            }
            // ==================================================================

            // == Now put the sortedList together
            // ===============================
            String srcTxt = sortList.get(0).getSrcText();
            if (sortList.size() > 1) {
                Collections.sort(sortList, GlossarySortCriteria.buildTargetComparator(Preferences.getPreferenceDefault(Preferences.GLOSSARY_PANE_SORT_ORDER, "APL;apl")));			
                MergedGlossaryEntries combineEntry = new MergedGlossaryEntries(srcTxt,sortList);
                returnList.add(combineEntry);
            } else {
                returnList.add(sortList.get(0));
            }
            // ==================================================================
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        return returnList;
    }
}
