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.io.File;
import java.io.FileFilter;
import java.util.List;
import java.util.zip.ZipOutputStream;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.io.exceptions.ZippingException;
import de.duehl.basics.text.Text;

/**
 * Diese Klasse kann Verzeichnisse in ein Zip-Archiv verpacken.
 *
 * @version 1.01     2024-04-26
 * @author Christian Dühl
 */

public class ZipDirectories extends Zip {

    /** Konstruktor. Hier taucht der letzte Verzeichnisname als erstes Verzeichnis im Archiv auf. */
    public ZipDirectories() {
        this(true);
    }

    /**
     * Konstruktor.
     *
     * @param withStartDirectory
     *            Gibt an, ob der letzte Verzeichnisname als erstes Verzeichnis im Archiv
     *            auftauchen soll oder nicht.
     */
    public ZipDirectories(boolean withStartDirectory) {
        super(withStartDirectory);
    }

    /**
     * Ein Verzeichnis in ein Zip-Archiv verpacken.
     *
     * @param zipFileName
     *            Name des Archivs (mit Pfad)
     * @param directory
     *            Zu packendes Verzeichnis.
     * @throws ZippingException
     *             Wenn das Archiv nicht angelegt werden kann, ein Fehler beim Packen auftritt oder
     *             das Archiv nicht geschlossen werden kann.
     */
    public void zipDirectory(String zipFileName, String directory) {
        ZipOutputStream zipOut = createArchive(zipFileName);
        addFilesOfOneDirectoryToArchive(directory, directory, zipOut);
        closeArchive(zipOut);
    }

    /**
     * Mehrere Verzeichnisse in ein Zip-Archiv verpacken.
     *
     * @param zipFileName
     *            Name des Archivs (mit Pfad)
     * @param directories
     *            Zu verpackende Verzeichnisse.
     * @throws ZippingException
     *             Wenn das Archiv nicht angelegt werden kann, ein Fehler beim Packen auftritt oder
     *             das Archiv nicht geschlossen werden kann.
     */
    public void zipMultipleDirectories(String zipFileName, String... directories) {
        zipMultipleDirectories(zipFileName, CollectionsHelper.stringArrayToList(directories));
    }

    /**
     * Mehrere Verzeichnisse in ein Zip-Archiv verpacken.
     *
     * @param zipFileName
     *            Name des Archivs (mit Pfad)
     * @param directories
     *            Liste der zu verpackenden Verzeichnisse.
     * @throws ZippingException
     *             Wenn das Archiv nicht angelegt werden kann, ein Fehler beim Packen auftritt oder
     *             das Archiv nicht geschlossen werden kann.
     */
    public void zipMultipleDirectories(String zipFileName, List<String> directories) {
        ZipOutputStream zipOut = createArchive(zipFileName);

        for (String directory : directories) {
            addFilesOfOneDirectoryToArchive(directory, directory, zipOut);
        }

        closeArchive(zipOut);
    }

    /**
     * Mehrere Verzeichnisse in ein Zip-Archiv verpacken, nach dem ersten wird die Änderung der
     * Last-Modified-Time, der Last-Access-Time und der Creation-Time deaktiviert.
     *
     * @param zipFileName
     *            Name des Archivs (mit Pfad)
     * @param directories
     *            Liste der zu verpackenden Verzeichnisse.
     * @throws ZippingException
     *             Wenn das Archiv nicht angelegt werden kann, ein Fehler beim Packen auftritt oder
     *             das Archiv nicht geschlossen werden kann.
     */
    public void zipMultipleDirectoriesAndSwitchTimeChangingOffAfterFirstDirectory(
            String zipFileName, List<String> directories) {
        ZipOutputStream zipOut = createArchive(zipFileName);

        for (String directory : directories) {
            addFilesOfOneDirectoryToArchive(directory, directory, zipOut);
            switchCreationLastAccessAndModificationTimeChangingOff();
        }

        closeArchive(zipOut);
    }

    /**
     * Packt die Dateien aus einem Verzeichnis in das Archiv.
     *
     * @param totalDirectory
     *            Zu packendes Basis-Verzeichnis.
     * @param directory
     *            Zu packendes Verzeichnis.
     * @param zipOut
     *            OutputStream für das Archiv.
     */
    private void addFilesOfOneDirectoryToArchive(String totalDirectory, String directory,
            ZipOutputStream zipOut) {
        String totalDirectoryCorr = Text.allSlashesToBackslashes(totalDirectory);
        String directoryCorr = Text.allSlashesToBackslashes(directory);

        if (!FileHelper.isDirectory(directoryCorr)) {
            throw new ZippingException("Ist kein Verzeichnis: " + directoryCorr);
        }
        File toZip = new File(directoryCorr);
        FileFilter allRegularFilesFilter = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isFile();
            }
        };
        for (File file : toZip.listFiles(allRegularFilesFilter)) {
            packFile(totalDirectoryCorr, zipOut, file);
        }
        FileFilter directotyFilesFilter = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory();
            }
        };
        for (File file : toZip.listFiles(directotyFilesFilter)) {
            addFilesOfOneDirectoryToArchive(totalDirectoryCorr, file.getPath(), zipOut);
        }
    }

}
