/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool
          with fuzzy matching, translation memory, keyword search,
          glossaries, and translation leveraging into updated projects.
          
 Copyright (C) 2022 Hiroshi Miura, Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: https://omegat.org/support
               
 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.io.FileUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import org.omegat.core.Core;
import org.omegat.core.data.ExternalTMX;
import org.omegat.core.data.IProject;
import org.omegat.core.data.NotLoadedProject;
import org.omegat.core.data.ProjectProperties;
import org.omegat.core.matching.NearString;
import org.omegat.core.matching.external.IExternalMemory;
import org.omegat.tokenizer.DefaultTokenizer;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.util.Language;
import org.omegat.util.OConsts;
import org.omegat.util.Preferences;


/** Tests behavior of FindMatches against option Preferences.EXT_TMX_KEEP_FOREIGN_MATCH **/
public class FindMatchesForeignLanguageTest {

    private static final File TMX_SAMPLE = new File("test/data/tmx/test-matches.tmx");
    
    private TestProject currentProject;

    @Before
    public void setUp() throws Exception {
        Core.initializeConsole(new TreeMap<>());
        Preferences.setPreference(Preferences.EXT_TMX_SHOW_LEVEL2, false);
        Preferences.setPreference(Preferences.EXT_TMX_USE_SLASH, false);
        Core.registerTokenizerClass(DefaultTokenizer.class); Core.registerTokenizerClass(DefaultTokenizer.class);
        
        ProjectProperties prop = new ProjectProperties(new File("."));
        prop.setSourceLanguage("fr"); prop.setTargetLanguage("de");
        prop.setSupportDefaultTranslations(true); prop.setSentenceSegmentingEnabled(false);
        Core.setProject(currentProject = new TestProject(prop, TMX_SAMPLE));
    }
        
    @Test
    public void testDefaultBehavior() {
        FindMatchesArray finder = new FindMatchesArray(new DefaultTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "Exemple", true, true, () -> false);
        assertEquals(1, result.size());
        assertEquals("Exemple", result.get(0).source);
        assertEquals("Beispiel", result.get(0).translation);
    }
    
    @Test
    public void testNoTargetNoFallback() {
        Preferences.setPreference(Preferences.EXT_TMX_KEEP_FOREIGN_MATCH, "30 false"); currentProject.reload();
        FindMatchesArray finder = new FindMatchesArray(new org.omegat.tokenizer.SnowballFrenchTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "langue allemande absente", true, true, () -> false);
        // In such a case, will return two results
        assertEquals(0, result.size());
    }
    
    @Test
    public void testNoTargetWithFallback() {
        Preferences.setPreference(Preferences.EXT_TMX_KEEP_FOREIGN_MATCH, "30 true"); currentProject.reload();
        FindMatchesArray finder = new FindMatchesArray(new org.omegat.tokenizer.SnowballFrenchTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "langue allemande absente", true, true, () -> false);
        // In such a case, will return two results
        assertEquals(2, result.size());
        assertEquals("Langue allemande absente", result.get(0).source);
        assertEquals("Langue allemande absente", result.get(1).source);
        assertTrue(result.get(0).translation.equals("Germana lingvo mankas"));
        assertTrue(result.get(1).translation.equals("German language missing"));
    }
    
    @Test
    public void testNoTargetPreferredLang() throws Exception {
        FindMatchesArray finder = new FindMatchesArray(new org.omegat.tokenizer.SnowballFrenchTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        // First option
        Preferences.setPreference(Preferences.EXT_TMX_KEEP_FOREIGN_MATCH, "eo 20; 30 true"); currentProject.reload();
        List<NearString> result = finder.search(Core.getProject(), "langue allemande absente", true, true, () -> false);
        assertEquals(1, result.size());
        assertEquals("Langue allemande absente", result.get(0).source);
        assertTrue(result.get(0).translation.equals("Germana lingvo mankas"));
        // Second option
        Preferences.setPreference(Preferences.EXT_TMX_KEEP_FOREIGN_MATCH, "en 20; 30 true"); currentProject.reload();
        result = finder.search(Core.getProject(), "langue allemande absente", true, true, () -> false);
        assertEquals(1, result.size());
        assertEquals("Langue allemande absente", result.get(0).source);
        assertEquals(result.get(0).translation, "German language missing");
    }

    @Test
    public void testTargetLanguageFallback() {
        FindMatchesArray finder = new FindMatchesArray(new DefaultTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "essai", true, true, () -> false);
        assertEquals(1, result.size());
        assertEquals("essai", result.get(0).source);
        assertEquals("Test", result.get(0).translation); // language de-DE
    }

    @Test
    public void testSourceLanguageFallbackWithTarget() {
        FindMatchesArray finder = new FindMatchesArray(new DefaultTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "Oui", true, true, () -> false);
        assertEquals(1, result.size());
        assertEquals("Oui", result.get(0).source); // language fr-FR
        assertEquals("Ja", result.get(0).translation);
    }
    
    @Test
    public void testSourceLanguageFallbackNoTarget() {
        Preferences.setPreference(Preferences.EXT_TMX_KEEP_FOREIGN_MATCH, "30 true");
        FindMatchesArray finder = new FindMatchesArray(new DefaultTokenizer(), new IExternalMemory[0], OConsts.MAX_NEAR_STRINGS, true, false);
        List<NearString> result = finder.search(Core.getProject(), "Ici", true, true, () -> false);
        assertEquals(1, result.size()); // does _not_ add entries with source language, even when we do source fallback!!!
        assertEquals("Ici", result.get(0).source); // language fr-FR
        assertEquals("here", result.get(0).translation);
    }

    static class TestProject extends NotLoadedProject implements IProject {
        private ProjectProperties prop;
        private File testTmx;

        public TestProject(final ProjectProperties prop, final File testTmx) {
            this.prop = prop;
            this.testTmx = testTmx;
        }
        
        @Override
        public ProjectProperties getProjectProperties() {
            return prop;
        }

        @Override
        public List<org.omegat.core.data.SourceTextEntry> getAllEntries() {
            return java.util.Collections.emptyList();
        }

        private Map<String, IExternalMemory> transMemories;
        
        @Override
        public Map<String, IExternalMemory> getTransMemories() {
            if (transMemories != null) return transMemories;
            transMemories = new TreeMap<>();
            try {
                ExternalTMX newTMX = new ExternalTMX(prop, testTmx, false, false);
                transMemories.put(testTmx.getPath(), newTMX);
            } catch (Exception ignored) {
            }
            return transMemories = Collections.unmodifiableMap(transMemories);
        }
        
        /** 
         * Reload the memories.
         * Since filtering of unit variants is done during load, not during search,
         * any change of option requires to reload the project to have effect !!!
         **/
        public void reload() {
            transMemories = null; getTransMemories();
        }
    }
}  
