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

 Copyright (C) 2016 Alex Buloichik
               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.gui.main;

import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.omegat.core.data.ProjectProperties;
import org.omegat.util.OConsts;
import org.omegat.util.StaticUtils;

/**
 * Class for support some MED-specific operations.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 */
public class ProjectMedProcessing {
    /**
     * It creates project internals from MED zip file.
     */
    public static void extractFromMed(File medZip, ProjectProperties props) throws Exception {
        String medName = medZip.getName().replaceAll("\\.zip$", "");

        // extract source and target languages
        Properties p = new Properties();
        try (ZipFile zip = new ZipFile(medZip)) {
            ZipEntry e = zip.getEntry(medName + "/dossier/workflow/translation");
            if (e == null) {
                throw new Exception("Wrong MED zip structure");
            }
            try (InputStream in = zip.getInputStream(e)) {
                p.load(in);
            }
        }
        // get source language
        String slang = p.getProperty("slang");
        if (slang == null) {
            throw new Exception("Bad MED format: slang not defined");
        }
        // get target language
        String tlang = p.getProperty("tlang");
        if (tlang == null) {
            throw new Exception("Bad MED format: tlang not defined");
        }
        props.setSourceLanguage(slang);
        props.setTargetLanguage(tlang);

        // copy source files
        try (ZipFile zip = new ZipFile(medZip)) {
            extractFiles(zip, medName + "/doc/main/" + props.getSourceLanguage().getLanguage() + "/", null,
                    new File(props.getSourceRoot()));
            extractFiles(zip, medName + "/doc/support/" + props.getSourceLanguage().getLanguage() + "/",
                    ".tmx", new File(props.getTMAutoRoot()));
            extractFiles(zip, medName + "/doc/support/" + props.getSourceLanguage().getLanguage() + "/term/",
                    null, new File(props.getGlossaryRoot()));
        }

        // copy original zip
        File outZip = new File(props.getProjectInternal(), "med/" + medZip.getName());
        outZip.getParentFile().mkdirs();
        FileUtils.copyFile(medZip, outZip);
    }

    private static void extractFiles(ZipFile zip, String zipPrefix, String zipSuffix, File projectDir)
            throws Exception {
        for (Enumeration<? extends ZipEntry> en = zip.entries(); en.hasMoreElements();) {
            ZipEntry e = en.nextElement();
            if (e.isDirectory()) {
                continue;
            }
            if (!e.getName().startsWith(zipPrefix)) {
                continue;
            }
            if (zipSuffix != null && !e.getName().endsWith(zipSuffix)) {
                continue;
            }
            File outFile = new File(projectDir, e.getName().substring(zipPrefix.length()));
            outFile.getParentFile().mkdirs();
            try (InputStream in = zip.getInputStream(e)) {
                FileUtils.copyInputStreamToFile(in, outFile);
            }
        }
    }

    /**
     * Finds one .zip file inside omegat/med/ dir.
     */
    public static File getOriginMedFile(ProjectProperties props) throws Exception {
        File[] files = new File(props.getProjectInternal(), "med/").listFiles(f -> f.isFile() && f.getName().endsWith(".zip"));
        if (files != null && files.length > 1) {
            throw new Exception("Too many source med files in the <project>/omegat/med directory");
        }
        File originMedFile = files != null && files.length > 0 ? files[0] : null;
        return originMedFile;
    }

    public static void createMed(File medZip, ProjectProperties props) throws Exception {
        if (medZip.getAbsolutePath().startsWith(props.getProjectRoot())) {
            throw new Exception("Med can't be inside project");
        }
        File sourceMedFile = getOriginMedFile(props);

        Properties translationContent = new Properties();

        String slang = props.getSourceLanguage().getLanguage();
        String tlang = props.getTargetLanguage().getLanguage();
        String medName = medZip.getName().replaceAll("\\.zip$", "");
        try (ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream(medZip))) {
            if (sourceMedFile != null) {
                String oldMedName = sourceMedFile.getName().replaceAll("\\.zip$", "");
                String[] skipPrefixes = new String[] { oldMedName + "/doc/main/" + slang + "/",
                        oldMedName + "/doc/main/" + tlang + "/" };
                String[] skipFiles = new String[] { oldMedName + "/dossier/workflow/translation",
                        oldMedName + "/doc/support/" + slang + "/tm/" + props.getProjectName()
                                + OConsts.LEVEL2_TMX + OConsts.TMX_EXTENSION,
                        oldMedName + "/doc/support/" + slang + "/term/glossary.txt" };

                try (ZipFile zip = new ZipFile(sourceMedFile)) {
                    // extract old file
                    ZipEntry tre = zip.getEntry(oldMedName + "/dossier/workflow/translation");
                    if (tre == null) {
                        throw new Exception("Wrong MED zip structure");
                    }
                    try (InputStream in = zip.getInputStream(tre)) {
                        translationContent.load(in);
                    }
                    for (Enumeration<? extends ZipEntry> en = zip.entries(); en.hasMoreElements();) {
                        ZipEntry e = en.nextElement();
                        if (e.isDirectory()) {
                            continue;
                        }
                        boolean add = true;
                        for (String skip : skipFiles) {
                            if (e.getName().equals(skip)) {
                                add = false;
                                break;
                            }
                        }
                        if (add) {
                            for (String skip : skipPrefixes) {
                                if (e.getName().startsWith(skip)) {
                                    add = false;
                                    break;
                                }
                            }
                        }
                        if (!add) {
                            continue;
                        }
                        String newName = medName + e.getName().substring(oldMedName.length());
                        outZip.putNextEntry(new ZipEntry(newName));
                        try (InputStream in = zip.getInputStream(e)) {
                            IOUtils.copy(in, outZip);
                        }
                    }
                }
            }
            translationContent.put("slang", slang);
            translationContent.put("tlang", tlang);
            outZip.putNextEntry(new ZipEntry(medName + "/dossier/workflow/translation"));
            translationContent.store(outZip, null);
            // source files
            packDir(new File(props.getSourceRoot()), medName + "/doc/main/" + slang + "/", outZip);
            // translations
            packDir(new File(props.getTargetRoot()), medName + "/doc/main/" + tlang + "/", outZip);

            // level2.tmx
            outZip.putNextEntry(new ZipEntry(medName + "/doc/support/" + slang + "/tm/"
                    + props.getProjectName() + OConsts.LEVEL2_TMX + OConsts.TMX_EXTENSION));
            FileUtils.copyFile(new File(props.getProjectRoot(),
                    props.getProjectName() + OConsts.LEVEL2_TMX + OConsts.TMX_EXTENSION), outZip);

            // statistics
            outZip.putNextEntry(new ZipEntry(medName + "/dossier/post/" + OConsts.STATS_FILENAME));
            FileUtils.copyFile(new File(props.getProjectInternal(), OConsts.STATS_FILENAME), outZip);

            // writable glossary
            File glosFile = new File(props.getWriteableGlossary());
            if (glosFile.exists()) {
                outZip.putNextEntry(new ZipEntry(medName + "/doc/support/" + slang + "/term/glossary.txt"));
                FileUtils.copyFile(glosFile, outZip);
            }
        }
    }

    private static void packDir(File dir, String namePrefix, ZipOutputStream zip) throws Exception {
        List<String> srcPathList = StaticUtils.buildRelativeFilesList(dir, null, null);
        for (String f : srcPathList) {
            if (f.startsWith("/")) {
                f = f.substring(1);
            }
            zip.putNextEntry(new ZipEntry(namePrefix + f));
            FileUtils.copyFile(new File(dir, f), zip);
        }
    }
}
