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

 Copyright (C) 2010 Wildrich Fourie, Alex Buloichik, Didier Briel
               2011 Didier Briel
               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.List;

import org.omegat.util.Preferences;

/**
 * Underlines all the terms in the SourceTextEntry that has matches in the
 * Glossary.
 * 
 * @author W. Fourie
 * @author Alex Buloichik
 * @author Didier Briel
 */
public class TransTips {
    // List containing the characters that may be found at the end of a word,
    // that
    // might result in the word not being underlined
    private static List<Integer> afterExcludeCases = new ArrayList<Integer>();
    static {
        afterExcludeCases.add((int) '.');
        afterExcludeCases.add((int) '!');
        afterExcludeCases.add((int) '?');
        afterExcludeCases.add((int) ':');
        afterExcludeCases.add((int) ',');
        afterExcludeCases.add((int) ';');
        afterExcludeCases.add((int) '<');
        afterExcludeCases.add((int) ')');
        afterExcludeCases.add((int) '-');
    };

    // List containing the characters that may be found at the beginning of a
    // word, that
    // might result in the word not being underlined
    private static List<Integer> beforeExcludeCases = new ArrayList<Integer>();
    static {
        beforeExcludeCases.add((int) '>');
        beforeExcludeCases.add((int) '(');
        beforeExcludeCases.add((int) ' ');
    };

    /**
     * Search for a word and returns the offset of the first occurrence.
     * Highlights are added for all occurrences found.
     * 
     * @param glossaryEntry
     *            To be searched
     * @param start
     *            Starting position
     * @param end
     *            Ending position
     * @return The offset of the first occurrence
     */
    public static void search(String sourceText, GlossaryEntry glossaryEntry, Search callback) {
        int firstOffset = -1;

        String word = glossaryEntry.getSrcText();
        // Test for invalid word.
        if (word == null || word.equals("")) {
            return;
        }

        String content = sourceText;

        if (!word.equals(word.toUpperCase())) { // The glossary word is not in full uppercase
            // We're comparing with lower case,
            // and the source text has to be in lower case too.
            content = sourceText.toLowerCase();
            word = word.toLowerCase();
        } // Otherwise, if the glossary word is in full uppercase,
          // we only apply transtips to source words that are in full uppercase too


        int lastIndex = 0;
        int wordSize = word.length();

        // Test for BadLocation
        while (((lastIndex = content.indexOf(word, lastIndex)) != -1)) {
            int endIndex = lastIndex + wordSize;
            if (Preferences.isPreference(Preferences.TRANSTIPS_EXACT_SEARCH)) {
                if (isWordAlone(content, lastIndex, word)) {
                    callback.found(glossaryEntry, lastIndex, endIndex);

                    if (firstOffset == -1)
                        firstOffset = lastIndex;
                }
            } else {
                callback.found(glossaryEntry, lastIndex, endIndex);

                if (firstOffset == -1)
                    firstOffset = lastIndex;

            }

            lastIndex = endIndex;
        }
        return;
    }

    /**
     * Determines if the word is surrounded by whitespace characters.
     * 
     * @param sourceText
     * @param testIndex
     * @param glosSrc
     * @return True if the word is alone; False if the word is contained within
     *         another word
     */
    public static boolean isWordAlone(String sourceText, int testIndex, String glosSrc) {
        // Remove directional characters
        sourceText = sourceText.replaceAll("[\u202A|\u202B|\u202C]", " ");

        // Check that word stands alone
        int before;
        try {
            before = sourceText.toLowerCase().codePointBefore(testIndex);
        } catch (Exception ex) {
            before = ' ';
        }

        // Test the end of the string
        int after;
        try {
            after = sourceText.toLowerCase().codePointAt(testIndex + glosSrc.length());
        } catch (Exception cp) {
            after = ' ';
        }

        // After Exclude Cases.
        if (afterExcludeCases.contains(after)) {
            after = ' ';
        }

        // Before Exclude Cases
        if (beforeExcludeCases.contains(before)) {
            before = ' ';
        }

        return before == ' ' && after == ' ';
    }

    public interface Search {
        void found(GlossaryEntry glossaryEntry, int start, int end);
    }
}
