/* FichothequeLib_Tools - Copyright (c) 2016-2021 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.tools.exportation.file;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import net.fichotheque.Fichotheque;
import net.fichotheque.addenda.Addenda;
import net.fichotheque.addenda.Document;
import net.fichotheque.addenda.Version;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.Fiches;
import net.mapeadores.util.io.FileUtils;
import net.mapeadores.util.io.IOUtils;
import net.mapeadores.util.logging.MessageHandler;
import net.fichotheque.junction.Junctions;


/**
 *
 * @author Vincent Calame
 */
public final class FileExportEngine {

    private final File destinationDir;
    private final MessageHandler messageHandler;
    private final Set<String> existingFileSet = new HashSet<String>();
    private final Set<String> doneSet = new HashSet<String>();
    private final Predicate<String> extensionPredicate;
    private final boolean symbolicLink = false;

    private FileExportEngine(File destinationDir, Predicate<String> extensionPredicate, MessageHandler messageHandler) {
        this.destinationDir = destinationDir;
        this.messageHandler = messageHandler;
        this.extensionPredicate = extensionPredicate;
        destinationDir.mkdirs();
        for (File f : destinationDir.listFiles()) {
            if (!f.isDirectory()) {
                existingFileSet.add(f.getName());
            }
        }
    }

    public static void run(File destinationDir, Addenda[] addendaArray, Predicate<String> extensionPredicate, MessageHandler messageHandler) {
        FileExportEngine engine = new FileExportEngine(destinationDir, extensionPredicate, messageHandler);
        for (Addenda addenda : addendaArray) {
            engine.exportAll(addenda);
        }
        engine.clear();
    }

    public static void run(File destinationDir, Addenda[] addendaArray, Predicate<String> extensionPredicate, MessageHandler messageHandler, Fiches fiches) {
        FileExportEngine engine = new FileExportEngine(destinationDir, extensionPredicate, messageHandler);
        for (Addenda addenda : addendaArray) {
            engine.exportFiches(addenda, fiches);
        }
        engine.clear();
    }

    private void exportAll(Addenda addenda) {
        for (Document document : addenda.getDocumentList()) {
            exportFile(document);
        }
    }

    private void exportFiches(Addenda addenda, Fiches fiches) {
        Fichotheque fichotheque = addenda.getFichotheque();
        for (Fiches.Entry fichesEntry : fiches.getEntryList()) {
            for (FicheMeta ficheMeta : fichesEntry.getFicheMetaList()) {
                Junctions junctions = fichotheque.getJunctions(ficheMeta, addenda);
                for (Junctions.Entry junctionEntry : junctions.getEntryList()) {
                    exportFile((Document) junctionEntry.getSubsetItem());
                }
            }
        }
    }

    private void clear() {
        for (String name : existingFileSet) {
            File f = new File(destinationDir, name);
            f.delete();
        }
    }

    private void exportFile(Document document) {
        String baseName = document.getBasename();
        if (doneSet.contains(baseName)) {
            return;
        } else {
            doneSet.add(baseName);
        }
        for (Version version : document.getVersionList()) {
            if (extensionPredicate.test(version.getExtension())) {
                String name = baseName + "." + version.getExtension();
                File destinationFile = new File(destinationDir, name);
                try {
                    if (destinationFile.exists()) {
                        if (destinationFile.isDirectory()) {
                            FileUtils.deleteDirectory(destinationFile);
                        } else {
                            destinationFile.delete();
                        }
                    }
                    version.linkTo(destinationFile.toPath(), symbolicLink);
                    logFileExport(destinationFile);
                } catch (IOException ioe) {
                    logIOException(ioe);
                } catch (UnsupportedOperationException uoe) {
                    try (InputStream is = version.getInputStream(); OutputStream os = new FileOutputStream(destinationFile)) {
                        IOUtils.copy(is, os);
                    } catch (IOException ioe2) {
                        logIOException(ioe2);
                    }
                }
                existingFileSet.remove(name);
            }
        }
    }

    private void logIOException(IOException ioe) {
        messageHandler.addMessage("severe.io", "_ error.exception.io_withmessage", ioe.getClass().getName(), ioe.getLocalizedMessage());
    }

    private void logFileExport(File file) {
        messageHandler.addMessage("info", "_ info.file", file);
    }

}
