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

 Copyright (C) 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.core.matching;

import java.util.List;

import org.omegat.gui.matches.MatchesVarExpansion;
import org.omegat.core.data.EntryKey;
import org.omegat.core.matching.external.IExternalMemory;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.util.OConsts;
import org.omegat.util.TMXProp;
import org.omegat.util.Preferences;

/**
 * Matcher which keeps almost one match in memory
 *
 * @author Thomas Cordonnier
 */
abstract class FindMatchesKeep extends FindMatches {    
    public FindMatchesKeep(ITokenizer sourceTokenizer, final IExternalMemory[] externalMemories, String memoryPath, boolean allowSeparateSegmentMatch, boolean searchExactlyTheSame) {
        super(sourceTokenizer, externalMemories, memoryPath, allowSeparateSegmentMatch, searchExactlyTheSame); 
    }
    
    /**
     * @param searchExactlyTheSame
     *            allows to search similarities with the same text as source segment. This mode used only for
     *            separate sentence match in paragraph project, i.e. where source is just part of current
     *            source.
     */
    public FindMatchesKeep(ITokenizer sourceTokenizer, final IExternalMemory[] externalMemories, boolean allowSeparateSegmentMatch, boolean searchExactlyTheSame) {
        super(sourceTokenizer, externalMemories, allowSeparateSegmentMatch, searchExactlyTheSame);
    }
    
    /** The result to be used to decide whenever we should insert or not **/
    protected abstract NearString lastKeptResult();
    
    protected abstract void doInsertion(final NearString added);
    
    private boolean isDisplayImprovedScore = Preferences.getPreferenceDefault(Preferences.EXT_TMX_MATCH_TEMPLATE, MatchesVarExpansion.DEFAULT_TEMPLATE).contains(MatchesVarExpansion.VAR_SCORE_IMPROVED);
    
    /**
     * Compare one entry with original entry.
     * 
     * @param candEntry
     *            entry to compare
     */
    protected void evalEntry(final EntryKey key, final String source, final String translation,
            String realSource, int totalPenalty, boolean fuzzy,
            NearString.MATCH_SOURCE comesFrom, final String tmxName,
            final String creator, final long creationDate, final String changer, final long changedDate,
            final String revisor, final String note, final List<TMXProp> props) {
        int similarityStem = -5, similarityNoStem = -5, simAdjusted = -5, scoreImproved = -5;
        NearString last = lastKeptResult();
        
        if (last == null) // insert if first score >= FUZZY_MATCH_THRESHOLD
            switch (this.sortKey) {
                case SCORE_STEM:
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    if (similarityStem < this.FUZZY_MATCH_THRESHOLD) return;
                    // Entry will be inserted, so calculate the two other scores
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    break;
                case SCORE_NO_STEM:
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    if (similarityNoStem < this.FUZZY_MATCH_THRESHOLD) return;
                    // Entry will be inserted, so calculate the two other scores
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    break;
                case IMPROVED_SCORE:
                    scoreImproved = calcSimilarityImproved(realSource) - totalPenalty;
                    if (scoreImproved < OConsts.FUZZY_MATCH_THRESHOLD) return;
                    // Then, act as adjusted score
                case ADJUSTED_SCORE:
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    if (simAdjusted < this.FUZZY_MATCH_THRESHOLD) return;
                    // Entry will be inserted, so calculate the two other scores
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    break;
            }
        else  // insert if first_score > last.first_score or (first_score == last.first_score and second_score >= last.second_score)
            switch (this.sortKey) {
                case SCORE_STEM:
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    if (similarityStem < last.scoreStem) return;
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    if ((similarityStem == last.scoreStem) && (similarityNoStem < last.scoreNoStem)) return;
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    break;
                case SCORE_NO_STEM:
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    if (similarityNoStem < last.scoreNoStem) return;
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    if ((similarityNoStem == last.scoreNoStem) && (similarityStem < last.scoreStem)) return;
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    if ((similarityNoStem == last.scoreNoStem) && (similarityStem == last.scoreStem) && (simAdjusted < last.adjustedScore)) return;
                    break;
                case IMPROVED_SCORE:
                    scoreImproved = calcSimilarityImproved(realSource) - totalPenalty;
                    if (scoreImproved < last.scoreImproved) return;
                    // Then, act as adjusted score
                case ADJUSTED_SCORE:
                    simAdjusted = calcSimilarityAdjusted(realSource) - totalPenalty;
                    if (simAdjusted < last.adjustedScore) return;
                    similarityStem = calcSimilarityStem(realSource) - totalPenalty;
                    if ((similarityStem == last.scoreStem) && (simAdjusted < last.adjustedScore)) return;
                    similarityNoStem = calcSimilarityNoStem(realSource) - totalPenalty;
                    if ((similarityStem == last.scoreStem) && (simAdjusted == last.adjustedScore) && (similarityNoStem < last.scoreNoStem)) return;
                    break;
            }
            
        // For performance reasons the improved score is calculated only if either it is selected as main score or if it is to be displayed
        // Else we can avoid it because it is not implied in sorting for other sorting methods
        if (isDisplayImprovedScore && (scoreImproved < 0))
            scoreImproved = calcSimilarityImproved(realSource) - totalPenalty;
        
        doInsertion(new NearString(key, source, translation, comesFrom, fuzzy, scoreImproved, similarityStem,
                similarityNoStem, simAdjusted, null, tmxName, creator, creationDate, changer,
                changedDate, revisor, note, props));
    }
}
