package com.yanzuoguang.util.helper;


import com.yanzuoguang.util.YzgError;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;


/**
 * ZIP压缩处理工具类
 *
 * @author 颜佐光
 */
public class ZipHelper {

    /**
     * @param dirFile
     * @param zipFile
     * @param isDir   是否显示最下级相对目录
     * @throws IOException
     * @创建日期:2018年5月4日 下午3:07:55
     * @作者: meter
     * @描述:压缩指定目录下所有文件
     * @Title: zipDirectory
     */
    public static void zipDirectory(File dirFile, File zipFile, boolean isDir) throws IOException {
        if (dirFile == null) {
            throw YzgError.getRuntimeException("018");
        }
        if (!dirFile.isDirectory()) {
            throw YzgError.getRuntimeException("019", dirFile.getName());
        }
        if (zipFile == null) {
            zipFile = new File(dirFile.getAbsolutePath() + ".zip");
        }
        String dirName = StringHelper.EMPTY;
        if (isDir) {
            dirName = dirFile.getName() + File.separator;
        }
        // 创建zip输出流
        ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(zipFile), StandardCharsets.UTF_8);
        try {
            // 创建缓冲输出流
            BufferedOutputStream bufferOutStream = new BufferedOutputStream(zipOutStream);
            try {
                dealDirFile(dirFile, dirName, bufferOutStream, zipOutStream);
            } finally {
                //最后关闭输出流
                bufferOutStream.close();
            }
        } finally {
            zipOutStream.close();
        }
    }

    /**
     * @param dirFile
     * @param zipFile
     * @throws IOException
     * @创建日期:2018年5月4日 下午3:07:55
     * @作者: meter
     * @描述:压缩指定目录下所有文件
     * @Title: zipDirectory
     */
    public static void zipDirectory(File dirFile, File zipFile) throws IOException {
        zipDirectory(dirFile, zipFile, false);
    }

    /**
     * 讲文件添加到新目录,并且添加一个新文件
     *
     * @param zipTo       目标ZIP
     * @param zipFrom     来源ZIP
     * @param sourcePath  下载路径
     * @param sourceFiles 需要添加的文件
     * @throws Exception
     */
    public static void zipTo(File zipTo, File zipFrom, String sourcePath, File... sourceFiles) throws IOException {
        // 创建zip输出流
        ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(zipTo), StandardCharsets.UTF_8);
        try {
            // 创建缓冲输出流
            BufferedOutputStream bufferOutStream = new BufferedOutputStream(zipOutStream);
            try {
                copyZip(zipFrom, zipOutStream, bufferOutStream, getExpand(sourcePath, sourceFiles));

                for (File sourceFile : sourceFiles) {
                    zipFile(sourcePath, sourceFile, zipOutStream, bufferOutStream);
                }
            } finally {
                //最后关闭输出流
                bufferOutStream.close();
            }
        } finally {
            zipOutStream.close();
        }
    }

    /**
     * 讲文件添加到新目录,并且添加一个新文件
     *
     * @param zipTo       目标ZIP
     * @param sourcePath  下载路径
     * @param sourceFiles 需要添加的文件
     * @throws Exception
     */
    public static void zip(File zipTo, String sourcePath, File... sourceFiles) throws IOException {
        System.out.println("待压缩文件:" + zipTo.getAbsolutePath());
        boolean exints = zipTo.exists();
        // 添加到已经存在的压缩文件中
        File tempFile = new File(zipTo.getAbsolutePath() + ".tmp");

        // 创建zip输出流
        ZipOutputStream zipOutStream;
        if (exints) {
            // 创建临时压缩文件
            zipOutStream = new ZipOutputStream(new FileOutputStream(tempFile), StandardCharsets.UTF_8);
        } else {
            // 新创建压缩文件
            zipOutStream = new ZipOutputStream(new FileOutputStream(zipTo), StandardCharsets.UTF_8);
        }
        try {
            // 创建缓冲输出流
            BufferedOutputStream bufferOutStream = new BufferedOutputStream(zipOutStream);
            try {
                if (exints) {
                    copyZip(zipTo, zipOutStream, bufferOutStream, getExpand(sourcePath, sourceFiles));
                }

                for (File sourceFile : sourceFiles) {
                    zipFile(sourcePath, sourceFile, zipOutStream, bufferOutStream);
                }
            } finally {
                //最后关闭输出流
                bufferOutStream.close();
            }
        } finally {
            zipOutStream.close();
        }

        // 假如存在,则删除临时文件,并更改临时文件名称
        if (tempFile.exists()) {
            boolean flag = zipTo.delete();
            if (flag) {
                tempFile.renameTo(zipTo);
            } else {
                throw YzgError.getRuntimeException("020", tempFile.getName());
            }
        }
    }

    /**
     * 获取需要排除的文件名和文件路径
     *
     * @param sourcePath   文件路径
     * @param sourceFiles  文件名
     * @return
     */
    private static List<String> getExpand(String sourcePath, File[] sourceFiles) {
        List<String> ret = new ArrayList<>();
        for (File sourceFile : sourceFiles) {
            ret.add(sourcePath + sourceFile.getName());
        }
        return ret;
    }

    /**
     * 讲压缩文件保存到另外一个压缩文件
     *
     * @param fromFile        来源文件
     * @param zipOutStream    目标文件
     * @param bufferOutStream 目标文件流
     * @param expandFiles     需要排除的文件
     * @throws IOException
     */
    private static void copyZip(File fromFile, ZipOutputStream zipOutStream, BufferedOutputStream bufferOutStream, List<String> expandFiles) throws IOException {
        ZipFile fromZipFile = new ZipFile(fromFile);

        Enumeration<? extends ZipEntry> fromEntries = fromZipFile.entries();
        while (fromEntries.hasMoreElements()) {
            ZipEntry fromEntry = fromEntries.nextElement();
            if (expandFiles.indexOf(fromEntry.getName()) > -1) {
                continue;
            }

            zipOutStream.putNextEntry(new ZipEntry(fromEntry.getName()));
            if (!fromEntry.isDirectory()) {
                write(fromZipFile.getInputStream(fromEntry), bufferOutStream);
            }
            zipOutStream.closeEntry();
        }
        fromZipFile.close();
    }

    /**
     * 执行文件压缩
     *
     * @param file            需要压缩的文件
     * @param zipOutStream    目标文件
     * @param bufferOutStream 目标文件流
     * @throws IOException
     */
    private static void zipFile(String sourcePath, File file, ZipOutputStream zipOutStream, BufferedOutputStream bufferOutStream) throws IOException {
        if (!file.exists()) {
            throw YzgError.getRuntimeException("021", file.getAbsolutePath());
        }
        // 创建压缩文件实体
        ZipEntry entry = new ZipEntry(sourcePath + file.getName());
        // 添加实体
        zipOutStream.putNextEntry(entry);
        // 创建输入流
        BufferedInputStream bufferInputStream = new BufferedInputStream(new FileInputStream(file));
        write(bufferInputStream, bufferOutStream);
        zipOutStream.closeEntry();
    }

    /**
     * @param inputStream
     * @param outStream
     * @throws IOException
     * @创建日期:2018年5月4日 下午2:09:37
     * @作者: meter
     * @描述:读写zip文件
     * @Title: write
     */
    private static void write(InputStream inputStream, OutputStream outStream) throws IOException {
        try {
            byte[] data = new byte[4096];
            int length = 0;
            while ((length = inputStream.read(data)) != -1) {
                outStream.write(data, 0, length);
            }
            outStream.flush();//刷新输出流
        } finally {
            inputStream.close();//关闭输入流
        }
    }

    /**
     * @param dirFile
     * @param parentDir
     * @param bufferOutStream
     * @param zipOutStream
     * @throws IOException
     * @创建日期:2018年5月4日 下午4:38:46
     * @作者: meter
     * @描述:处理目录文件
     * @Title: dealDirFile
     */
    private static void dealDirFile(File dirFile, String parentDir, BufferedOutputStream bufferOutStream, ZipOutputStream zipOutStream) throws IOException {
        File[] fileList = dirFile.listFiles();
        if (fileList == null) {
            return;
        }
        for (File file : fileList) {
            if (file.isFile()) {
                // 创建压缩文件实体
                ZipEntry entry = new ZipEntry(parentDir + file.getName());
                // 添加实体
                zipOutStream.putNextEntry(entry);
                // 创建输入流
                BufferedInputStream bufferInputStream = new BufferedInputStream(new FileInputStream(file));
                write(bufferInputStream, bufferOutStream);
            } else {
                dealDirFile(file, parentDir + file.getName() + File.separator, bufferOutStream, zipOutStream);
            }
        }
    }

    /**
     * @param dirPath
     * @param zipPath
     * @throws IOException
     * @创建日期:2018年5月4日 下午3:22:11
     * @作者: meter
     * @描述:重载zipDirectory
     * @Title: zipDirectory
     */
    public static void zipDirectory(String dirPath, String zipPath) throws IOException {
        if (zipPath == null || "".equals(zipPath)) {
            zipDirectory(new File(dirPath), null);
        } else {
            zipDirectory(new File(dirPath), new File(zipPath));
        }
    }
    //------------------------------------------------米特华丽的分割线----------------------------------------------------------------------------------------

    /**
     * @param zipFile
     * @param destDir
     * @throws IOException
     * @创建日期:2018年5月4日 下午4:00:41
     * @作者: meter
     * @描述:解压文件
     * @Title: unzip
     */
    public static void unzip(File zipFile, File destDir) throws IOException {
        ZipFile zipOutFile = new ZipFile(zipFile, Charset.forName("gbk"));
        Enumeration<? extends ZipEntry> entries = zipOutFile.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            if (entry.isDirectory()) {
                File tempFile = new File(destDir.getAbsolutePath() + File.separator + entry.getName());
                if (!tempFile.exists()) {
                    tempFile.mkdirs();
                }
            } else {
                File tempFile = new File(destDir.getAbsolutePath() + File.separator + entry.getName());
                checkParentDir(tempFile);
                FileOutputStream fileOutStream = new FileOutputStream(tempFile);
                BufferedOutputStream bufferOutStream = new BufferedOutputStream(fileOutStream);
                write(zipOutFile.getInputStream(entry), bufferOutStream);
                bufferOutStream.close();
                fileOutStream.close();
            }
        }
        zipOutFile.close();//记得关闭zip文件
    }

    /**
     * @param file
     * @创建日期:2018年5月4日 下午4:37:41
     * @作者: meter
     * @描述:验证父目录是否存在,否则创建
     * @Title: checkParentDir
     */
    private static void checkParentDir(File file) {
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
    }

}