package com.thinkbiganalytics.feedmgr.support;

/*-
 * #%L
 * thinkbig-feed-manager-controller
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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.
 * #L%
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Utility to interact with zip files
 */
public class ZipFileUtil {


    /**
     * Validate filenames in a zip file This does case insensitive comparison
     */
    public static boolean validateZipEntries(byte[] zipFile, Set<String> validNames, Set<String> requiredNames, boolean matchAllValidNames) throws IOException {
        if (validNames == null) {
            validNames = new HashSet<>();
        }
        List<String> validNamesList = validNames.stream().map(String::toLowerCase).collect(Collectors.toList());
        Set<String> fileNames = getFileNames(zipFile).stream().map(String::toLowerCase).collect(Collectors.toSet());

        boolean isValid = fileNames != null && !fileNames.isEmpty() && validNamesList.stream().allMatch(fileNames::contains);
        if (isValid && matchAllValidNames) {
            isValid = fileNames.size() == validNames.size();
        }

        if (isValid && requiredNames != null && !requiredNames.isEmpty()) {
            isValid = requiredNames.stream().allMatch(fileNames::contains);
        }
        return isValid;
    }


    /**
     *
     * @param zipFile
     * @param validNames
     * @param requiredNames
     * @return
     * @throws IOException
     */
    public static boolean validateZipEntriesWithRequiredEntries(byte[] zipFile, Set<String> validNames, Set<String> requiredNames) throws IOException {
        return validateZipEntries(zipFile, validNames, requiredNames, false);
    }

    /**
     *
     * @param zipFile
     * @param validNames
     * @param matchAllValidNames
     * @return
     * @throws IOException
     */
    public static boolean validateZipEntries(byte[] zipFile, Set<String> validNames, boolean matchAllValidNames) throws IOException {
        return validateZipEntries(zipFile, validNames, null, matchAllValidNames);
    }

    /**
     * Gets the file names in a zip file
     */
    public static Set<String> getFileNames(byte[] zipFile) throws IOException {
        Set<String> fileNames = new HashSet<>();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(zipFile);
        ZipInputStream zis = new ZipInputStream(inputStream);
        ZipEntry entry;
        while ((entry = zis.getNextEntry()) != null) {
            fileNames.add(entry.getName());
        }
        return fileNames;
    }


    /**
     * Adds an entry to a zip file
     *
     * @param zip      the zip file which will have the content added
     * @param file     the string to add to the zip
     * @param fileName the zip file name
     * @return the zip file with the newly added content
     */
    public static byte[] addToZip(byte[] zip, String file, String fileName) throws IOException {
        InputStream zipInputStream = new ByteArrayInputStream(zip);
        ZipInputStream zis = new ZipInputStream(zipInputStream);
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {

            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {

                ByteArrayOutputStream out = new ByteArrayOutputStream();
                int len = 0;
                while ((len = zis.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
                out.close();

                zos.putNextEntry(entry);
                zos.write(out.toByteArray());
                zos.closeEntry();

            }
            zis.closeEntry();
            zis.close();

            entry = new ZipEntry(fileName);
            zos.putNextEntry(entry);
            zos.write(file.getBytes());
            zos.closeEntry();
        }
        return baos.toByteArray();
    }
}

