package de.duehl.basics.io.zip;

/*
 * Copyright 2024 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.util.ArrayList;
import java.util.List;

import de.duehl.basics.io.FileHelper;
import de.duehl.basics.system.starter.ProgramStarter;
import de.duehl.basics.text.Text;

/**
 * Diese Klasse kann Zip-Dateien mit Hilfe des Programms 7zip entpacken.
 *
 * @version 1.01     2024-11-26
 * @author Christian Dühl
 */

public class UnzipWith7ZipDirectProgramStart {

    private static final boolean DEBUG = false;

    /** Das 7zip-Executable mit Pfad. */
    private final String sevenZipExecutable;

    /** Die Liste mit den Namen der beim Verpacken zusätzlich angelegten Dateien. */
    private List<String> createdFilenames;

    /**
     * Gibt an, ob die erzeugten Dateien (out und err) neben das Zip-File gelegt werden (default).
     * Anderenfalls muss man ein Verzeichnis angeben.
     */
    private boolean useZipFileDirForCreatedFiles;

    /**
     * Das Verzeichnis für die erzeugten Dateien (out und err). Nur relevant, falls
     * useZipFileDirForCreatedFiles false ist.
     */
    private String dirForCreatedFiles;

    /** Gibt an, ob das Archiv entschlüsselt werden soll. */
    private boolean cypher;

    /** Das Password im Fall, dass entschlüsselt werden soll. */
    private String password;

    /** Konstruktor, nutzt den Default-Pfad für 7zip. */
    public UnzipWith7ZipDirectProgramStart() {
        this(ZipFilesWith7Zip.DEFAULT_PATH_TO_7_ZIP);
    }

    /**
     * Konstruktor.
     *
     * @param sevenZipExecutable
     *            Das 7zip-Executable mit Pfad.
     */
    public UnzipWith7ZipDirectProgramStart(String sevenZipExecutable) {
        this.sevenZipExecutable = sevenZipExecutable;

        createdFilenames = new ArrayList<>();
        useZipFileDirForCreatedFiles = true;
        cypher = false;

        say("sevenZipExecutable = " + sevenZipExecutable);
    }

    /** Gibt an, dass die Datei entschlüsselt werden soll. */
    public void cypher(String password) {
        if (password.contains(" ")) {
            throw new IllegalArgumentException("Das Passwort darf keine Leerzeichen beinhalten, "
                    + "weil sonst die Verwendung auf der Kommandozeile nicht wie gewünscht "
                    + "funktioniert.");
        }
        cypher = true;
        this.password = password;
        say("cypher = " + cypher);
        say("password ... wurde gesetzt");
    }

    /**
     * Setzt das Verzeichnis für die erzeugten Dateien (out und err).
     *
     * Wird diese Methode nicht benutzt, so werden die Dateien neben das Zip-File gelegt.
     */
    public void setDirForCreatedFiles(String dirForCreatedFiles) {
        useZipFileDirForCreatedFiles = false;
        this.dirForCreatedFiles = dirForCreatedFiles;
        say("useZipFileDirForCreatedFiles = " + useZipFileDirForCreatedFiles);
        say("dirForCreatedFiles = " + dirForCreatedFiles);
    }

    /**
     * Führt das Entpacken des Ziparchivs durch.
     *
     * @param archiveFilename
     *            Name der zu entpackenden Archivdatei (endet auf zip oder 7z).
    * @param targetDirectory
    *            Das Verzeichnis in das entpackt wird.
     */
    public void unzip(String archiveFilename, String targetDirectory) {
        List<String> parameters = createParametersString(archiveFilename, targetDirectory);

        determineDirForCreatedFiles(archiveFilename);
        String bareZipName = FileHelper.getBareName(archiveFilename);
        String logBareFilename = "unzip_" + FileHelper.exchangeExtension(bareZipName, ".txt");
        String errBareFilename = "unzip_" + FileHelper.exchangeExtension(bareZipName, ".err");
        String logFilename = FileHelper.concatPathes(dirForCreatedFiles, logBareFilename);
        String errorLogfilename = FileHelper.concatPathes(dirForCreatedFiles, errBareFilename);

        say("starte Programm ... ");
        ProgramStarter starter = new ProgramStarter(sevenZipExecutable, parameters,
                logFilename, errorLogfilename);
        starter.runAndWait(); // wartet wie der Name sagt darauf, dass
                              // der Prozess fertig ist!
        say("Programm beendet");

        createdFilenames.add(logFilename);
        createdFilenames.add(errorLogfilename);
    }

    private List<String> createParametersString(String archiveFilename, String targetDirectory) {
        List<String> parameters = new ArrayList<>();

        parameters.add("x");

        if (cypher) {
            parameters.add("-p" + password);
        }

        if (archiveFilename.contains(" ")) {
            parameters.add("\"" + archiveFilename + "\"");
        }
        else {
            parameters.add(archiveFilename);
        }

        if (targetDirectory.contains(" ")) {
            parameters.add("-o" + "\"" + targetDirectory + "\"");
        }
        else {
            parameters.add("-o" + targetDirectory);
        }

        say("parameters = " + parameters);
        return parameters;
    }

    private void determineDirForCreatedFiles(String archiveFilename) {
        if (useZipFileDirForCreatedFiles) {
            dirForCreatedFiles = FileHelper.getDirName(archiveFilename);;
        }
        say("dirForCreatedFiles = " + dirForCreatedFiles);
    }

    /** Getter für die Liste mit den Namen der beim Verpacken zusätzlich angelegten Dateien. */
    public List<String> getCreatedFilenames() {
        return createdFilenames;
    }

    private static void say(String message) {
        if (DEBUG) {
            Text.say(message);
        }
    }

}
