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

               2013 Thomas Cordonnier
 Copyright (C) 2014 Alex Buloichik, Didier Briel
               2015 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.data;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.omegat.core.matching.external.IBrowsableMemory;
import org.omegat.util.StringUtil;
import org.omegat.util.TMXProp;

/**
 * Utility class for import translations from tm/auto/ files.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Didier Briel
 * @author Thomas Cordonnier
 */
public class ImportFromAutoTMX {
    final RealProject project;
    /** Map of all segments in project by source text. Just for optimize some processes. */
    Map<String, List<SourceTextEntry>> existEntries = new HashMap<String, List<SourceTextEntry>>();

    public ImportFromAutoTMX(RealProject project, List<SourceTextEntry> allProjectEntries) {
        this.project = project;
        for (SourceTextEntry ste : allProjectEntries) {
            List<SourceTextEntry> list = existEntries.get(ste.getSrcText());
            if (list == null) {
                list = new ArrayList<SourceTextEntry>();
                existEntries.put(ste.getSrcText(), list);
            }
            list.add(ste);
        }
    }

    /**
     * Process a TMX from an automatic folder
     * @param mem The memory to process
     * @param isEnforcedTMX If true, existing default translations will be overwritten in all cases
     */
    void process(IBrowsableMemory mem, boolean isEnforcedTMX) throws Exception {
               
        for (PrepareTMXEntry e : mem.getEntries()) { // iterate by all entries in TMX
            List<SourceTextEntry> list = existEntries.get(e.source);
            if (list == null) {
                continue; // there is no entries for this source
            }
            boolean hasKey = e.hasPropValue(ProjectTMX.PROP_FILE, null)
                        || e.hasPropValue(ProjectTMX.PROP_ID, null)
                        || e.hasPropValue(ProjectTMX.PROP_PREV, null)
                        || e.hasPropValue(ProjectTMX.PROP_NEXT, null)
                        || e.hasPropValue(ProjectTMX.PROP_PATH, null);
            EntryKey tmxKey = hasKey ? new EntryKey (
                e.getPropValue(ProjectTMX.PROP_FILE),
                e.source,
                e.getPropValue(ProjectTMX.PROP_ID),
                e.getPropValue(ProjectTMX.PROP_PREV),
                e.getPropValue(ProjectTMX.PROP_NEXT),
                e.getPropValue(ProjectTMX.PROP_PATH)) : null;
            for (SourceTextEntry ste : list) { // for each TMX entry - get all sources in project
                TMXEntry existTranslation = project.getTranslationInfo(ste);

                String id = ste.getKey().id;
                boolean hasICE = id != null && e.hasPropValue(ProjectTMX.PROP_XICE, id);
                boolean has100PC = id != null && e.hasPropValue(ProjectTMX.PROP_X100PC, id);

                if (!hasICE && !has100PC) {// TMXEntry without x-ids
                    if (hasKey) if (!ste.getKey().equals(tmxKey)) continue; // populate only this key
                    if (!hasKey && !existTranslation.defaultTranslation) continue; // alternative exist - skip
                    if (isAltTranslation(e) && !altTranslationMatches(e, ste.getKey())) {
                        // TMX entry is an alternative translation that does not match this STE.
                        continue;
                    }
                    if (existTranslation.isTranslated()) { // default translation already exist
                        if (existTranslation.linked == TMXEntry.ExternalLinked.xAUTO
                                && !StringUtil.equalsWithNulls(existTranslation.translation, e.translation) 
                                || isEnforcedTMX) {
                            // translation already from auto and really changed or translation comes 
                            // from the enforce folder
                            setTranslation(ste, e, !hasKey, TMXEntry.ExternalLinked.xAUTO);
                        }
                    } else {
                        // default translation not exist - use from auto tmx
                        setTranslation(ste, e, !hasKey, TMXEntry.ExternalLinked.xAUTO);
                    }
                } else {// TMXEntry with x-ids
                    if (!existTranslation.isTranslated() || existTranslation.defaultTranslation) {
                        // need to update if id in xICE or x100PC
                        if (hasICE) {
                            setTranslation(ste, e, false, TMXEntry.ExternalLinked.xICE);
                        } else if (has100PC) {
                            setTranslation(ste, e, false, TMXEntry.ExternalLinked.x100PC);
                        }
                    } else if (existTranslation.linked == TMXEntry.ExternalLinked.xICE
                            || existTranslation.linked == TMXEntry.ExternalLinked.x100PC) {
                        // already contains x-ice
                        if (hasICE
                                && !StringUtil.equalsWithNulls(existTranslation.translation, e.translation)) {
                            setTranslation(ste, e, false, TMXEntry.ExternalLinked.xICE);
                        }
                    } else if (existTranslation.linked == TMXEntry.ExternalLinked.x100PC) {
                        // already contains x-100pc
                        if (has100PC
                                && !StringUtil.equalsWithNulls(existTranslation.translation, e.translation)) {
                            setTranslation(ste, e, false, TMXEntry.ExternalLinked.x100PC);
                        }
                    }
                }
            }
        }
    }
    
    private boolean isAltTranslation(PrepareTMXEntry entry) {
        if (entry.otherProperties == null) return false;
        boolean hasFileProp = false;
        boolean hasOtherProp = false;
        for (TMXProp p : entry.otherProperties) {
            if (p.getType().equals(ProjectTMX.PROP_FILE)) {
                hasFileProp = true;
            } else if (p.getType().equals(ProjectTMX.PROP_ID)
                    || p.getType().equals(ProjectTMX.PROP_NEXT)
                    || p.getType().equals(ProjectTMX.PROP_PATH)
                    || p.getType().equals(ProjectTMX.PROP_PREV)) {
                hasOtherProp = true;
            }
        }
        return EntryKey.IGNORE_FILE_CONTEXT ? hasFileProp && hasOtherProp : hasFileProp;
    }
    
    private boolean altTranslationMatches(PrepareTMXEntry entry, EntryKey key) {
        try {
            for (TMXProp p : entry.otherProperties) {
                if (p.getType().equals(ProjectTMX.PROP_FILE) && EntryKey.IGNORE_FILE_CONTEXT) {
                    continue;
                }
                Field f = EntryKey.class.getField(p.getType());
                Object value = f.get(key);
                if (!value.equals(p.getValue())) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    private void setTranslation(SourceTextEntry entry, PrepareTMXEntry trans, boolean defaultTranslation,
            TMXEntry.ExternalLinked externalLinked) {
        if (StringUtil.isEmpty(trans.note)) {
            trans.note = null;
        }

        trans.source = entry.getSrcText();

        TMXEntry newTrEntry;

        if (trans.translation == null && trans.note == null) {
            // no translation, no note
            newTrEntry = null;
        } else {
            newTrEntry = new TMXEntry(trans, defaultTranslation, externalLinked);
        }
        project.projectTMX.setTranslation(entry, newTrEntry, defaultTranslation);
    }
}
