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

 Copyright (C) 2012-2015 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

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

 This program 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, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 **************************************************************************/

package org.omegat.core.machinetranslators;

import java.util.Map;
import java.util.TreeMap;
import java.io.IOException;
import java.io.File;

import org.omegat.core.Core;
import org.omegat.core.data.PrepareTMXEntry;
import org.omegat.core.data.ExternalTMX;
import org.omegat.gui.exttrans.IMachineTranslation;
import org.omegat.util.Language;
import org.omegat.util.Preferences;
import org.omegat.util.Log;
import org.omegat.util.OConsts;
import org.openide.awt.Mnemonics;
import org.omegat.util.DirectoryMonitor;
import org.omegat.core.events.IProjectEventListener;
import org.omegat.core.CoreEvents;

import org.omegat.core.matching.FuzzyMatcher;
import org.omegat.core.matching.ISimilarityCalculator;
import org.omegat.core.matching.LevenshteinDistance;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.util.Token;


/**
 * Reads TMX files 'mt/*.tmx', and implements IMachineTranslation: <br>
 * 	for each input, gives the translation if found in any TMX, else return null.
 * 
 * @author Thomas CORDONNIER
 */
public class LocalTranslate extends BaseTranslate implements IProjectEventListener {

    public static final int MIN_MATCHING = 80;

    private DirectoryMonitor tmMonitor;
    private Map<String, ExternalTMX> transMemories = new TreeMap<String, ExternalTMX>();
    {
        CoreEvents.registerProjectChangeListener(this);
        if (Core.getProject() instanceof org.omegat.core.data.RealProject) onProjectChanged(PROJECT_CHANGE_TYPE.LOAD);
    }

    public String getName() {
        return "Local";
    }

    @Override
    protected String getPreferenceName() {
        return Preferences.ALLOW_LOCALE_TRANSLATE;
    }

    public void onProjectChanged(PROJECT_CHANGE_TYPE eventType) {
        switch (eventType) {
        case LOAD:
            try {
                loadTM();
                tok = Core.getProject().getSourceTokenizer();
            } catch (Exception e) {
                e.printStackTrace();
            }
            break;
        }
    }
    
    /**
     * Locates and loads external TMX files with legacy translations. Uses directory monitor for check file
     * updates.
     */
    private void loadTM() throws IOException {
        final File tmRoot = new File(Core.getProject().getProjectProperties().getProjectRoot() + OConsts.DEFAULT_MACHINE_TRANSLATIONS + File.separator);
        if (! tmRoot.exists()) return;
        tmMonitor = new DirectoryMonitor(tmRoot, new DirectoryMonitor.Callback() {
            public void fileChanged(File file) {
                if (!file.getName().endsWith(OConsts.TMX_EXTENSION)) {
                    // not a TMX file
                    return;
                }
                // create new translation memories map
                Map<String, ExternalTMX> newTransMemories = new TreeMap<String, ExternalTMX>(transMemories);
                if (file.exists()) {
                    try {
                        Log.log ("LocalTranslate : Load " + file);
                        ExternalTMX newTMX = new ExternalTMX(Core.getProject().getProjectProperties(), file,
                                Preferences.isPreference(Preferences.EXT_TMX_SHOW_LEVEL2),
                                Preferences.isPreference(Preferences.EXT_TMX_USE_SLASH));
                        newTransMemories.put(file.getPath(), newTMX);
                        Log.log ("LocalTranslate : Load " + file + " ok.");
                    } catch (Exception e) {
                        String filename = file.getPath();
                        Log.logErrorRB(e, "TF_TM_LOAD_ERROR", filename);
                        Core.getMainWindow().displayErrorRB(e, "TF_TM_LOAD_ERROR", filename);
                    }
                } else {
                    newTransMemories.remove(file.getPath());
                }
                transMemories = newTransMemories;
            }
        });
        tmMonitor.checkChanges();
        tmMonitor.start();
    }
    
    private ITokenizer tok;

    /**
     * Translate.
     * 
     * @param sLang
     *            source language
     * @param tLang
     *            target language
     * @param text
     *            text for translation
     * @return translated text, or null if translation impossible
     */
    public String translate(Language sLang, Language tLang, String text) {
        Token[] strTokensStem = tok.tokenizeWords(text, ITokenizer.StemmingMode.MATCHING);
        final ISimilarityCalculator distance = new LevenshteinDistance(); // distance is not thread-safe
        String result = null; int max = -1; 
        for (ExternalTMX tmx: transMemories.values())
            for (PrepareTMXEntry entry: tmx.getEntries()) {
                Token[] candTokens = tok.tokenizeWords(entry.source, ITokenizer.StemmingMode.MATCHING);
                int similarityStem = FuzzyMatcher.calcSimilarity(distance, strTokensStem, candTokens);
                if (similarityStem > MIN_MATCHING && similarityStem > max) {
                    result = entry.translation; max = similarityStem;
                }
            }
        return result;
    }

    public boolean isEnabled() {
        return true;
    }
}
