using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip;
// ReSharper disable All

namespace Team.Utility
{
    public class ZipUtility
    {
        /// <summary>
        /// ZIP:压缩单个文件
        /// add yuangang by 2016-06-13
        /// </summary>
        /// <param name="fileToZip">需要压缩的文件(绝对路径)</param>
        /// <param name="zipPath">压缩后的文件路径(绝对路径)</param>
        /// <param name="zippedFileName">压缩后的文件名称(文件名,默认 同源文件同名)</param>
        /// <param name="compressionLevel">压缩等级(0 无 - 9 最高,默认 5)</param>
        /// <param name="blockSize">缓存大小(每次写入文件大小,默认 2048)</param>
        /// <param name="isEncrypt">是否加密(默认 加密)</param>
        public static void Zip(string fileToZip, string zipPath, string zippedFileName = "",
            int compressionLevel = 5, int blockSize = 2048, bool isEncrypt = true)
        {
            //如果文件没有找到,则报错
            if (!File.Exists(fileToZip))
            {
                throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
            }

            //文件名称(默认同源文件名称相同)
            var zipFileName = string.IsNullOrEmpty(zippedFileName)
                ? zipPath + "\\" +
                  new FileInfo(fileToZip).Name.Substring(0, new FileInfo(fileToZip).Name.LastIndexOf('.')) + ".zip"
                : zipPath + "\\" + zippedFileName + ".zip";

            using (var zipFile = File.Create(zipFileName))
            {
                using (var zipStream = new ZipOutputStream(zipFile))
                {
                    using (var streamToZip = new FileStream(fileToZip, FileMode.Open, FileAccess.Read))
                    {
                        var fileName = fileToZip.Substring(fileToZip.LastIndexOf("\\", StringComparison.Ordinal) + 1);

                        var zipEntry = new ZipEntry(fileName);

                        if (isEncrypt)
                        {
                            //压缩文件加密
                            zipStream.Password = "123";
                        }

                        zipStream.PutNextEntry(zipEntry);

                        //设置压缩级别
                        zipStream.SetLevel(compressionLevel);

                        //缓存大小
                        var buffer = new byte[blockSize];

                        int sizeRead;
                        do
                        {
                            sizeRead = streamToZip.Read(buffer, 0, buffer.Length);
                            zipStream.Write(buffer, 0, sizeRead);
                        } while (sizeRead > 0);

                        streamToZip.Close();
                    }

                    zipStream.Finish();
                    zipStream.Close();
                }

                zipFile.Close();
            }
        }

        /// <summary>
        /// ZIP:压缩文件夹
        /// 
        /// </summary>
        /// <param name="directoryToZip">需要压缩的文件夹(绝对路径)</param>
        /// <param name="zipedPath">压缩后的文件路径(绝对路径)</param>
        /// <param name="zipedFileName">压缩后的文件名称(文件名,默认 同源文件夹同名)</param>
        /// <param name="isEncrypt">是否加密(默认 加密)</param>
        public static void ZipDirectory(string directoryToZip, string zipedPath, string zipedFileName = "",
            bool isEncrypt = true)
        {
            //如果目录不存在,则报错
            if (!Directory.Exists(directoryToZip))
            {
                throw new FileNotFoundException("指定的目录: " + directoryToZip + " 不存在!");
            }

            //文件名称(默认同源文件名称相同)
            var zipFileName = string.IsNullOrEmpty(zipedFileName)
                ? zipedPath + "\\" + new DirectoryInfo(directoryToZip).Name + ".zip"
                : zipedPath + "\\" + zipedFileName + ".zip";

            using (var zipFile = File.Create(zipFileName))
            {
                using (var s = new ZipOutputStream(zipFile))
                {
                    if (isEncrypt)
                    {
                        //压缩文件加密
                        s.Password = "123";
                    }

                    ZipSetp(directoryToZip, s, "");
                }
            }
        }

        /// <summary>
        /// ZIP:压缩文件夹
        /// </summary>
        /// <param name="directoryToZip">需要压缩的文件夹(绝对路径)</param>
        /// <param name="zipedPath">压缩后的文件路径(绝对路径)</param>
        /// <param name="zipedFileName">压缩后的文件名称(文件名,默认 同源文件夹同名)</param>
        /// <param name="isEncrypt">是否加密(默认 加密)</param>
        public static async Task ZipDirectoryAsync(string directoryToZip, string zipedPath, string zipedFileName = "",
            bool isEncrypt = true)
        {
            //如果目录不存在,则报错
            if (!Directory.Exists(directoryToZip))
            {
                throw new FileNotFoundException("指定的目录: " + directoryToZip + " 不存在!");
            }

            //文件名称(默认同源文件名称相同)
            var zipFileName = string.IsNullOrEmpty(zipedFileName)
                ? zipedPath + "\\" + new DirectoryInfo(directoryToZip).Name + ".zip"
                : zipedPath + "\\" + zipedFileName + ".zip";

            using var zipFile = File.Create(zipFileName);
            using var s = new ZipOutputStream(zipFile);
            if (isEncrypt)
            {
                //压缩文件加密
                s.Password = "123";
            }

            await ZipSetpAsync(directoryToZip, s, "");
        }

        /// <summary>
        /// 压缩多层目录
        /// </summary>
        /// <param name="strDirectory">The directory.</param>
        /// <param name="zipedFile">The ziped file.</param>
        public static void ZipFileDirectory(string strDirectory, string zipedFile)
        {
            using (System.IO.FileStream ZipFile = System.IO.File.Create(zipedFile))
            {
                using (ZipOutputStream s = new ZipOutputStream(ZipFile))
                {
                    ZipSetp(strDirectory, s, "");
                }
            }
        }
        public static void ZipFiles(List<string> filesPath,string zipedPath,string zipedFileName = "",
        bool isEncrypt = true)
        {
            //如果目录不存在,则报错
            foreach (var filePath in filesPath)
            {
                throw new FileNotFoundException("指定的目录: " + filePath + " 不存在!");
            }

            //文件名称(默认同源文件名称相同)
            var zipFileName = string.IsNullOrEmpty(zipedFileName)
                ? zipedPath + "\\" + new DirectoryInfo(filesPath.First()).Name + ".zip"
                : zipedPath + "\\" + zipedFileName + ".zip";

            using (var zipFile = File.Create(zipFileName))
            {
                using (var s = new ZipOutputStream(zipFile))
                {
                    if (isEncrypt)
                    {
                        //压缩文件加密
                        s.Password = "123";
                    }

                    ZipSetp(filesPath.First(), s, "");
                }
            }
        }

        public static async Task ZipFilesAsync(List<string> filesPathes, string zipedPath, string zipedFileName = "",
            int compressionLevel = 5, int blockSize = 2048, bool isEncrypt = true)
        {
            foreach (var filesPath in filesPathes)
            {
                //如果文件没有找到,则报错
                if (!File.Exists(filesPath))
                {
                    throw new FileNotFoundException("指定要压缩的文件: " + filesPath + " 不存在!");
                }
            }
           

            //文件名称(默认同源文件名称相同)
            var zipFileName = string.IsNullOrEmpty(zipedFileName)
                ? zipedPath + "\\" +
                  new FileInfo(zipedPath).Name.Substring(0, new FileInfo(zipedPath).Name.LastIndexOf('.')) + ".zip"
                : zipedPath + "\\" + zipedFileName + ".zip";

            using (var zipFile = File.Create(zipFileName))
            {
                using (var zipStream = new ZipOutputStream(zipFile))
                {
                    foreach (var filesPath in filesPathes)
                    {
                        using (var streamToZip = new FileStream(filesPath, FileMode.Open, FileAccess.Read))
                        {
                            var fileName =
                                filesPath.Substring(filesPath.LastIndexOf("\\", StringComparison.Ordinal) + 1);

                            var zipEntry = new ZipEntry(fileName);

                            if (isEncrypt)
                            {
                                //压缩文件加密
                                zipStream.Password = "123";
                            }

                            zipStream.PutNextEntry(zipEntry);

                            //设置压缩级别
                            zipStream.SetLevel(compressionLevel);

                            //缓存大小
                            var buffer = new byte[blockSize];

                            int sizeRead;
                            do
                            {
                                sizeRead = await streamToZip.ReadAsync(buffer, 0, buffer.Length);
                                await zipStream.WriteAsync(buffer, 0, sizeRead);
                            } while (sizeRead > 0);
                        }

                    }
                 
                }
            }
        }

        /// <summary>
        /// 递归遍历目录
        /// add yuangang by 2016-06-13
        /// </summary>
        private static void ZipSetp(string strDirectory, ZipOutputStream s, string parentPath)
        {
            if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
            {
                strDirectory += Path.DirectorySeparatorChar;
            }

            var crc = new Crc32();

            var fileNames = Directory.GetFileSystemEntries(strDirectory);

            foreach (var file in fileNames) // 遍历所有的文件和目录
            {

                if (Directory.Exists(file)) // 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
                {
                    var pPath = parentPath;
                    pPath += file.Substring(file.LastIndexOf("\\", StringComparison.Ordinal) + 1);
                    pPath += "\\";
                    ZipSetp(file, s, pPath);
                }

                else // 否则直接压缩文件
                {
                    //打开压缩文件
                    using (var fs = File.OpenRead(file))
                    {

                        var buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);

                        var fileName = parentPath +
                                       file.Substring(file.LastIndexOf("\\", StringComparison.Ordinal) + 1);
                        var entry = new ZipEntry(fileName);

                        entry.DateTime = DateTime.Now;
                        entry.Size = fs.Length;

                        fs.Close();

                        crc.Reset();
                        crc.Update(buffer);

                        entry.Crc = crc.Value;
                        s.PutNextEntry(entry);

                        s.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }
        
        
        private static async Task ZipSetpAsync(string strDirectory, ZipOutputStream s, string parentPath)
        {
            if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
            {
                strDirectory += Path.DirectorySeparatorChar;
            }

            var crc = new Crc32();

            var fileNames = Directory.GetFileSystemEntries(strDirectory);

            foreach (var file in fileNames) // 遍历所有的文件和目录
            {

                if (Directory.Exists(file)) // 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
                {
                    var pPath = parentPath;
                    pPath += file.Substring(file.LastIndexOf("\\", StringComparison.Ordinal) + 1);
                    pPath += "\\";
                    ZipSetp(file, s, pPath);
                }

                else // 否则直接压缩文件
                {
                    //打开压缩文件

                    using var fs = File.OpenRead(file);
                    var buffer = new byte[fs.Length];
                    await fs.ReadAsync(buffer, 0, buffer.Length);

                    var fileName = parentPath +
                                   file.Substring(file.LastIndexOf("\\", StringComparison.Ordinal) + 1);
                    var entry = new ZipEntry(fileName);

                    entry.DateTime = DateTime.Now;
                    entry.Size = fs.Length;
                    crc.Reset();
                    crc.Update(buffer);
                    entry.Crc = crc.Value;
                    s.PutNextEntry(entry);
                    await s.WriteAsync(buffer, 0, buffer.Length);
                }
            }
        }
        /// <summary>
        /// ZIP:解压一个zip文件
        /// add yuangang by 2016-06-13
        /// </summary>
        /// <param name="zipFile">需要解压的Zip文件(绝对路径)</param>
        /// <param name="targetDirectory">解压到的目录</param>
        /// <param name="password">解压密码</param>
        /// <param name="overWrite">是否覆盖已存在的文件</param>
        public static void UnZip(string zipFile, string targetDirectory, string password, bool overWrite = true)
        {
            //如果解压到的目录不存在,则报错
            if (!Directory.Exists(targetDirectory))
            {
                throw new FileNotFoundException("指定的目录: " + targetDirectory + " 不存在!");
            }
            //目录结尾
            if (!targetDirectory.EndsWith("\\")) { targetDirectory = targetDirectory + "\\"; }

            using (var zipFiles = new ZipInputStream(File.OpenRead(zipFile)))
            {
                zipFiles.Password = password;
                ZipEntry theEntry;

                while ((theEntry = zipFiles.GetNextEntry()) != null)
                {
                    var directoryName = "";
                    var pathToZip = theEntry.Name;

                    if (pathToZip != "")
                        directoryName = Path.GetDirectoryName(pathToZip) + "\\";

                    var fileName = Path.GetFileName(pathToZip);

                    Directory.CreateDirectory(targetDirectory + directoryName);

                    if (fileName == "") continue;
                    if ((!File.Exists(targetDirectory + directoryName + fileName) || !overWrite) &&
                        (File.Exists(targetDirectory + directoryName + fileName))) continue;
                    using (var streamWriter = File.Create(targetDirectory + directoryName + fileName))
                    {
                        var data = new byte[2048];
                        while (true)
                        {
                            var size = zipFiles.Read(data, 0, data.Length);

                            if (size > 0)
                                streamWriter.Write(data, 0, size);
                            else
                                break;
                        }
                        streamWriter.Close();
                    }
                }
                zipFiles.Close();
            }
        }
    }
}