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

 Copyright (C) 2021 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.statistics;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.omegat.core.Core;
import org.omegat.core.data.IProject;
import org.omegat.core.data.IProject.FileInfo;
import org.omegat.core.data.ProjectProperties;
import org.omegat.core.data.ProtectedPart;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.data.TMXEntry;
import org.omegat.core.threads.LongProcessThread;
import org.omegat.util.OConsts;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;
import org.omegat.util.StaticUtils;
import org.omegat.util.gui.TextUtil;

/**
 * Thread for calculate revision statistics.
 * 
 * @author Thomas Cordonnier
 */
public class CalcRevisionStatistics extends LongProcessThread {
    private static final String[] htHeaders = new String[] { "", OStrings.getString("CT_STATS_Segments"),
            OStrings.getString("CT_STATS_Words"), OStrings.getString("CT_STATS_Characters_NOSP"),
            OStrings.getString("CT_STATS_Characters"), OStrings.getString("CT_STATS_Files") };

    private static final String[] htRows = new String[] { OStrings.getString("CT_STATS_Total"),
            OStrings.getString("CT_STATS_Unique"),
            OStrings.getString("CT_STATS_Revised"), OStrings.getString("CT_STATS_RevisedChanged") };
    private static final boolean[] htAlign = new boolean[] { false, true, true, true, true, true };

    private static final String[] ftHeaders = new String[] { OStrings.getString("CT_STATS_FILE_Name"),
            OStrings.getString("CT_STATS_FILE_Total_Segments"),
            OStrings.getString("CT_STATS_FILE_Unique_Segments"),
            OStrings.getString("CT_STATS_Revised"), OStrings.getString("CT_STATS_RevisedChanged") };

    private static final boolean[] ftAlign = new boolean[] { false, true, true, true, true  };

    private final IStatisticsPanel.Standard callback;

    public CalcRevisionStatistics(IStatisticsPanel.Standard callback) {
        this.callback = callback;
    }

    @Override
    public void run() {
        IProject p = Core.getProject();
        String result = buildProjectStats(p, callback);
        callback.setTextData(result);
        callback.finishData();

      /*  String internalDir = p.getProjectProperties().getProjectInternal();
        // removing old stats
        try {
            File oldstats = new File(internalDir + "word_counts");
            if (oldstats.exists())
                oldstats.delete();
        } catch (Exception e) {
        }

        // now dump file based word counts to disk
        String fn = internalDir + OConsts.STATS_FILENAME;
        Statistics.writeStat(fn, result);
        callback.setDataFile(fn); */
    }

    /** Convenience method */
    public static String buildProjectStats(final IProject project) {
        return buildProjectStats(project, null);
    }
    
    /**
     * Builds a file with statistic info about the project. The total word &
     * character count of the project, the total number of unique segments, plus
     * the details for each file.
     */
    public static String buildProjectStats(final IProject project, final IStatisticsPanel.Standard callback) {

        FileData global = new FileData();

        // find unique segments
        Map<String, SourceTextEntry> uniqueSegment = new HashMap<String, SourceTextEntry>();
        Set<SourceTextEntry> revised = new HashSet<>(), changed = new HashSet<>();
        for (SourceTextEntry ste : project.getAllEntries()) {
            String src = ste.getSrcText();
            for (ProtectedPart pp : ste.getProtectedParts())
                src = src.replace(pp.getTextInSourceSegment(), pp.getReplacementUniquenessCalculation());
            if (!uniqueSegment.containsKey(src)) uniqueSegment.put(src, ste);
            TMXEntry tr = project.getTranslationInfo(ste);
            if ((tr != null) && (tr.revisor != null)) revised.add(ste);
            if ((tr != null) && (tr.changer != null)
                && (! tr.changer.equals(Preferences.getPreferenceDefault(Preferences.TEAM_AUTHOR, System.getProperty("user.name"))))) changed.add(ste);
        }
        Set<String> filesUnique = new HashSet<String>();
        Set<String> filesRemainingUnique = new HashSet<String>();
        for (Map.Entry<String, SourceTextEntry> en : uniqueSegment.entrySet()) {
            /* Number of words and chars calculated without all tags and protected parts. */
            StatCount count = new StatCount(en.getValue());

            // add to unique
            global.unique.add(count);
            filesUnique.add(en.getValue().getKey().file);
        }
        global.unique.addFiles(filesUnique.size());
        for (SourceTextEntry ste: revised) global.revised.add(new StatCount(ste));
        for (SourceTextEntry ste: changed) global.changed.add(new StatCount(ste));

        List<FileData> counts = new ArrayList<FileData>();
        Map<String, Boolean> firstSeenUniqueSegment = new HashMap<String, Boolean>();
        for (FileInfo file : project.getProjectFiles()) {
            FileData numbers = new FileData();
            numbers.filename = file.filePath;
            counts.add(numbers);
            int fileTotal = 0;
            for (SourceTextEntry ste : file.entries) {
                String src = ste.getSrcText();
                for (ProtectedPart pp : ste.getProtectedParts())
                    src = src.replace(pp.getTextInSourceSegment(), pp.getReplacementUniquenessCalculation());

                /* Number of words and chars calculated without all tags and protected parts. */
                StatCount count = new StatCount(ste);

                // add to total
                global.total.add(count);
                fileTotal = 1;

                // add to file's info
                numbers.total.add(count);

                Boolean firstSeen = firstSeenUniqueSegment.get(src);
                if (firstSeen == null) {
                    firstSeenUniqueSegment.put(src, false);
                    numbers.unique.add(count);
                }

                TMXEntry tr = project.getTranslationInfo(ste);
                if ((tr != null) && (tr.revisor != null)) numbers.revised.add(count);
                if ((tr != null) && (tr.changer != null)
                    && (! tr.changer.equals(Preferences.getPreferenceDefault(Preferences.TEAM_AUTHOR, System.getProperty("user.name"))))) numbers.changed.add(count);
            }
            global.total.addFiles(fileTotal);
        }

        StringBuilder result = new StringBuilder();

        result.append(OStrings.getString("CT_STATS_Project_Statistics"));
        result.append("\n\n");

        String[][] headerTable = calcHeaderTable(new StatCount[] { global.total, global.unique, global.revised, global.changed });
        if (callback != null) callback.setProjectTableData(htHeaders, headerTable);
        result.append(TextUtil.showTextTable(htHeaders, headerTable, htAlign));
        result.append("\n\n");

        // STATISTICS BY FILE
        result.append(OStrings.getString("CT_STATS_FILE_Statistics"));
        result.append("\n\n");
        String[][] filesTable = calcFilesTable(project.getProjectProperties(), counts);
        if (callback != null) callback.setFilesTableData(ftHeaders, filesTable);
        result.append(TextUtil.showTextTable(ftHeaders, filesTable, ftAlign));

        return result.toString();
    }

    protected static String[][] calcHeaderTable(final StatCount[] result) {
        String[][] table = new String[result.length][6];

        for (int i = 0; i < result.length; i++) {
            table[i][0] = htRows[i];
            table[i][1] = Integer.toString(result[i].segments);
            table[i][2] = Integer.toString(result[i].words);
            table[i][3] = Integer.toString(result[i].charsWithoutSpaces);
            table[i][4] = Integer.toString(result[i].charsWithSpaces);
            table[i][5] = Integer.toString(result[i].files);
        }
        return table;
    }

    protected static String[][] calcFilesTable(final ProjectProperties m_config, final List<FileData> counts) {
        String[][] table = new String[counts.size()][5];

        int r = 0;
        for (FileData numbers : counts) {
            table[r][0] = StaticUtils.makeFilenameRelative(numbers.filename, m_config.getSourceRoot());
            table[r][1] = Integer.toString(numbers.total.segments);
            table[r][2] = Integer.toString(numbers.unique.segments);
            table[r][3] = Integer.toString(numbers.revised.segments);
            table[r][4] = Integer.toString(numbers.changed.segments);
            r++;
        }
        return table;
    }

    public static class FileData {
        public String filename;
        public StatCount total, unique, revised, changed;

        public FileData() {
            total = new StatCount();
            unique = new StatCount();
            revised = new StatCount();
            changed = new StatCount();
        }
    }
}
