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

 Copyright (C) 2008 Alex Buloichik
               2010 Didier Briel
               2013 Thomas Cordonnier
               2014-2015 Alex Buloichik, 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.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.omegat.core.matching.external.IExternalMemory;
import org.omegat.core.statistics.StatisticsInfo;
import org.omegat.core.team.IRemoteRepository;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.util.Language;

/**
 * Interface for access to loaded project. Each loaded project will be new instance of IProject.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Didier Briel
 * @author Thomas Cordonnier
 */
public interface IProject {

    /**
     * Save project properties only.
     */
    void saveProjectProperties() throws Exception;

    /**
     * Save project.
     */
    void saveProject();

    void saveProject(boolean doTeamSync);

    /**
     * Close project.
     */
    void closeProject();

    /**
     * Create translated documents.
     */
    void compileProject(String sourcePattern) throws Exception;

    default Exception lastCompileException() { return null; }
    default void startCompilation() {}
    
    /**
     * Get project properties.
     * 
     * @return project properties
     */
    ProjectProperties getProjectProperties();
    
    /**
     * Returns repository for team project, or null.
     */
    IRemoteRepository getRepository();

    /**
     * Get project loaded status.
     * 
     * @return true if project loaded
     */
    boolean isProjectLoaded();

    /**
     * Is project modified ?
     */
    boolean isProjectModified();

    /**
     * Returns tokenizer for source language.
     */
    ITokenizer getSourceTokenizer();

    /**
     * Returns tokenizer for target language.
     */
    ITokenizer getTargetTokenizer();

    /**
     * Get all source segments. It's unmodifiable list, so, there is no need synchronization to read it.
     */
    List<SourceTextEntry> getAllEntries();

    /**
     * Set translation for entry.
     * 
     * Optimistic locking will not be checked.
     * 
     * @param entry
     *            entry
     * @param trans
     *            translation. It can't be null
     */
    void setTranslation(SourceTextEntry entry, PrepareTMXEntry trans, boolean defaultTranslation,
            TMXEntry.ExternalLinked externalLinked);

    /**
     * Set translation for entry with optimistic lock checking: if previous translation is not the same like
     * in storage, OptimisticLockingFail exception will be generated. Use when user has typed a new
     * translation.
     * 
     * @param entry
     *            entry
     * @param trans
     *            translation. It can't be null
     */
    void setTranslation(SourceTextEntry entry, PrepareTMXEntry trans, boolean defaultTranslation,
            TMXEntry.ExternalLinked externalLinked, AllTranslations previousTranslations)
            throws OptimisticLockingFail;

    /**
     * Change note only for translation.
     * 
     * @param entry
     *            entry
     * @param oldTrans
     *            old translation
     * @param note
     *            note text
     */
    void setNote(SourceTextEntry entry, TMXEntry oldTrans, String note);

    /**
     * Get statistics for project.
     * 
     * @return
     */
    StatisticsInfo getStatistics();

    /**
     * Get translation info for specified entry. It looks first for multiple, then for default. This method
     * ALWAYS returns TMXEntry, because note can exist even for non-translated segment. Use
     * TMXEntry.isTranslated() for check if translation text really exist. Translation can be checked for
     * default/alternative by the TMXEntry.defaultTranslation.
     * 
     * @param ste
     *            source entry
     * @return translation
     */
    TMXEntry getTranslationInfo(SourceTextEntry ste);

    /**
     * Get default and alternative translations for optimistic locking.
     */
    AllTranslations getAllTranslations(SourceTextEntry ste);

    /**
     * Iterate by all default translations in project.
     */
    void iterateByDefaultTranslations(DefaultTranslationsIterator it);

    /**
     * Iterate by all multiple translations in project.
     */
    void iterateByMultipleTranslations(MultipleTranslationsIterator it);

    /**
     * Check if orphaned.
     */
    boolean isOrphaned(String source);

    /**
     * Check if orphaned.
     */
    boolean isOrphaned(EntryKey entry);

    /**
     * Get all translation memories from /tm/ folder.
     * 
     * @return translation memories
     */
    Map<String, IExternalMemory> getTransMemories();
    
    /**
     * Get all translation memories from /other_lang/ folder.
     * 
     * @return translation memories
     */
    Map<Language, ProjectTMX> getOtherTargetLanguageTMs();

    /**
     * Get info about each source file in project. It's unmodifiable list, so, there is no need
     * synchronization to read it.
     */
    List<FileInfo> getProjectFiles();

    /**
     * For a given source file, calculate the path of the target file that would be created
     * by running Create Translated Documents (the file may not exist yet).
     * <p>
     * The target path must be calculated because it can depend on project properties such
     * as the target language, etc.
     * 
     * @param sourceFile The relative path (under the <code>source</code> directory) of the
     * source file, e.g. <code>Bundle.properties</code>
     * @return The relative path (under the <code>target</code> directory) of the corresponding
     * target file, e.g. <code>Bundle_fr_FR.properties</code>
     */
    String getTargetPathForSourceFile(String sourceFile);
    
    /**
     * Get ordered list of source file names.
     */
    List<String> getSourceFilesOrder();

    /**
     * Set ordered list of source file names.
     */
    void setSourceFilesOrder(List<String> filesList);

    public static class FileInfo {
        public String filePath;
        /**
         * IFilter implementing Class that was used to parse the file
         */
        public Class<?> filterClass;
        /**
         * Human readable name of the file format as defined by the filter.
         */
        public String filterFileFormatName;
        /**
         * Characterset name used for parsing the source file.
         */
        public String fileEncoding;
        public List<SourceTextEntry> entries = new ArrayList<SourceTextEntry>();
    }

    public interface DefaultTranslationsIterator {
        void iterate(String source, TMXEntry trans);
    }

    public interface MultipleTranslationsIterator {
        void iterate(EntryKey source, TMXEntry trans);
    }

    /**
     * These translations can't be null. Only value or EMPTY_TRANSLATION.
     */
    public class AllTranslations {
        protected TMXEntry defaultTranslation;
        protected TMXEntry alternativeTranslation;
        protected TMXEntry currentTranslation;

        public TMXEntry getDefaultTranslation() {
            return defaultTranslation;
        }

        public TMXEntry getAlternativeTranslation() {
            return alternativeTranslation;
        }

        public TMXEntry getCurrentTranslation() {
            return currentTranslation;
        }
    }

    /**
     * Exception for optimistic locking fail. Used when segment changed remotely or by automatic translation,
     * but user also changed data.
     */
    public class OptimisticLockingFail extends Exception {
        private final String oldTranslationText;
        private final String newTranslationText;
        private final AllTranslations previous;

        public OptimisticLockingFail(String oldTranslationText, String newTranslationText,
                AllTranslations previous) {
            this.oldTranslationText = oldTranslationText;
            this.newTranslationText = newTranslationText;
            this.previous = previous;
        }

        public String getOldTranslationText() {
            return oldTranslationText;
        }

        public String getNewTranslationText() {
            return newTranslationText;
        }

        public AllTranslations getPrevious() {
            return previous;
        }
    }
}
