/*
 * Copyright 2022 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.uninstaller.utils;

import org.apache.commons.lang3.SystemUtils;
import org.jboss.installer.actions.ActionException;
import org.jboss.installer.core.MnemonicUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;

import static org.jboss.uninstaller.Uninstaller.UNINSTALL_PROPERTIES_FILE;

/**
 * A collection of jar utilities
 *
 * @author mshikalw@redhat.com
 */
public class JarUtils {

    private static final int BUFFER_SIZE = 10240;

    public JarUtils() {
    }

    /**
     * Add jar contents to the deployment archive under the given prefix
     */
    public static void addJar(JarOutputStream outputStream, Path jar) throws IOException {
        try (JarInputStream jis = new JarInputStream(new FileInputStream(jar.toFile()))) {
            JarEntry entry = jis.getNextJarEntry();
            while (entry != null) {
                if (!entry.isDirectory()) {
                    String entryName = entry.getName();
                    if (isFilterFiles(entryName)) {
                        addJarEntry(outputStream, entryName, jis);
                    }
                }
                entry = jis.getNextJarEntry();
            }
        }
    }

    /**
     * Add a jar entry to the deployment archive
     */
    private static void addJarEntry(JarOutputStream outputStream, String entryName, InputStream inputStream)
            throws IOException {
        outputStream.putNextEntry(new JarEntry(entryName));
        copyStream(outputStream, inputStream);
    }

    /**
     * Copies the input stream to the output stream
     */
    private static void copyStream(OutputStream outputStream, InputStream inputStream) throws IOException {
        byte[] bytes = new byte[BUFFER_SIZE];
        int read = inputStream.read(bytes, 0, 4096);
        while (read > 0) {
            outputStream.write(bytes, 0, read);
            read = inputStream.read(bytes, 0, 4096);
        }
    }

    /**
     * Adds uninstaller properties file to the jar
     */
    public static void addUninstallerPropertyFile(Path targetPath, JarOutputStream jarOutputStream) throws IOException {
        File file = new File(targetPath.toAbsolutePath() + File.separator + UNINSTALL_PROPERTIES_FILE);
        try {
            try (FileWriter writer = new FileWriter(file)) {
                writer.write(targetPath.toAbsolutePath().toString());
                writer.flush();
            }

            ZipEntry zipEntry = new ZipEntry(file.getName());
            jarOutputStream.putNextEntry(zipEntry);
            try (FileInputStream in = new FileInputStream(file)) {
                copyStream(jarOutputStream, in);
            }
        } finally {
            if (file.exists()) {
                file.delete();
            }
        }
    }

    public static void jarDirectory(Path dirOrFile2jar, JarOutputStream jos) throws ActionException {
        try {
            jarDirectory(dirOrFile2jar, jos, null);
        } catch (IOException e) {
            throw new ActionException("Unable to create an uninstaller jar :", e);
        }
    }
    /**
     * creates jar from a directory
     */
    private static void jarDirectory(Path dirOrFile2jar, JarOutputStream jos, String path) throws IOException {
        if (Files.isDirectory(dirOrFile2jar)) {
            File[] dirList = dirOrFile2jar.toFile().listFiles();
            String subPath = (path == null) ? "" : (path + dirOrFile2jar.toFile().getName() + "/");
            if (path != null) {
                if(isFilterFiles(path)) {
                    JarEntry je = new JarEntry(subPath);
                    je.setTime(dirOrFile2jar.toFile().lastModified());
                    jos.putNextEntry(je);
                    jos.flush();
                    jos.closeEntry();
                }
            }
            for (int i = 0; i < dirList.length; i++) {
                File f = dirList[i];
                jarDirectory(f.toPath(), jos, subPath);
            }
        } else {
            if(isFilterFiles(path)) {
                try (FileInputStream fis = new FileInputStream(dirOrFile2jar.toFile())) {
                    final String prefix = SystemUtils.IS_OS_WINDOWS?path.replace(File.separator, "/"):path;
                    JarEntry entry = new JarEntry(prefix + dirOrFile2jar.getFileName());
                    entry.setTime(dirOrFile2jar.toFile().lastModified());
                    jos.putNextEntry(entry);
                    copyStream(jos, fis);
                    jos.closeEntry();
                }
            }
        }

    }

    private static boolean isFilterFiles(String entryName) {
        // if we're filtering JAR entries separator is always "/"
        return (entryName.startsWith("org/jboss/uninstaller/") ||
                entryName.startsWith(MnemonicUtils.class.getName().replace('.', '/')) ||
                entryName.startsWith("i18n") || entryName.startsWith("img"));
    }
}