using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SystemX.Net.Platform.Common.Util
{
    public static class FileSystemUtil
    {
        public static void DeleteFile(string strSourcePath)
        {
            if (!File.Exists(strSourcePath))
                return;

            File.Delete(strSourcePath);            
        }
        public static void CopyToDestination(string strSourcePath, string strDesitinationPath, bool bDeleteSrc, bool bOverwrite)
        {
            if (!Directory.Exists(Path.GetDirectoryName(strDesitinationPath)))
                Directory.CreateDirectory(Path.GetDirectoryName(strDesitinationPath));

            File.Copy(strSourcePath, strDesitinationPath, bOverwrite);
            
            while(true)
            {
                FileInfo fileSrc = new FileInfo(strSourcePath);
                FileInfo fileDest = new FileInfo(strDesitinationPath);

                if (fileSrc.Length == fileDest.Length)
                    break;

                Thread.Sleep(1);
            }

            if (bDeleteSrc)
                DeleteFile(strSourcePath);
        }

        public static void Compress(DirectoryInfo directorySelected, string strFilePath)
        {
            using (var fileStream = new FileStream(strFilePath, FileMode.OpenOrCreate))
            {
                using (ZipArchive zip = new ZipArchive(fileStream, ZipArchiveMode.Create))
                {
                    foreach (FileInfo file in directorySelected.GetFiles())
                    {
                        var entry = zip.CreateEntry(file.Name, CompressionLevel.Optimal);
                        using (Stream s = entry.Open())
                        {
                            FileStream file2Comp = new FileStream(file.FullName, FileMode.Open);

                            byte[] data = new byte[file2Comp.Length];
                            file2Comp.Read(data, 0, (int)file2Comp.Length);
                            s.Write(data, 0, data.Length);

                            file2Comp.Close();
                            s.Close();
                        }
                    }
                }

                fileStream.Close();
            }
        }

        public static void AddFile2CompFile(List<string> vstrTargetFilePath, string strCompFilePath)
        {
            using (var fileStream = new FileStream(strCompFilePath, FileMode.OpenOrCreate))
            {
                using (ZipArchive zip = new ZipArchive(fileStream, ZipArchiveMode.Create))
                {
                    foreach (string strFilePath in vstrTargetFilePath)
                    {
                        string strTargetFileName = Path.GetFileName(strFilePath);
                        var entry = zip.CreateEntry(strTargetFileName, CompressionLevel.Optimal);

                        using (Stream s = entry.Open())
                        {
                            FileStream file2Comp = new FileStream(strFilePath, FileMode.Open);

                            byte[] data = new byte[file2Comp.Length];
                            file2Comp.Read(data, 0, (int)file2Comp.Length);
                            s.Write(data, 0, data.Length);

                            file2Comp.Close();
                            s.Close();
                        }
                    }
                }

                fileStream.Close();
            }
        }

        public static void CompressStream2CompFile(List<KeyValuePair<string, MemoryStream>> vPairTargetData, string strCompFilePath)
        {
            using (var fileStream = new FileStream(strCompFilePath, FileMode.OpenOrCreate))
            {
                using (ZipArchive zip = new ZipArchive(fileStream, ZipArchiveMode.Create))
                {
                    foreach (KeyValuePair<string, MemoryStream> dataBinFile in vPairTargetData)
                    {
                        var entry = zip.CreateEntry(dataBinFile.Key, CompressionLevel.Optimal);

                        using (Stream s = entry.Open())
                        {
                            byte[] data = new byte[dataBinFile.Value.Length];
                            s.Write(dataBinFile.Value.ToArray(), 0, data.Length);

                            s.Close();
                        }
                    }
                }
            }
        }

        public static void Decompress(string strPathZip, string strPath)
        {
            if (!Directory.Exists(strPath))
                Directory.CreateDirectory(strPath);

            using (ZipArchive archive = ZipFile.OpenRead(strPathZip))
            {
                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                    entry.ExtractToFile(Path.Combine(strPath, entry.FullName), true);
                }
            }
        }

        public static List<string> GetFileList(string strPathZip)
        {
            List<string> vstrFileNames = new List<string>();

            using (ZipArchive archive = ZipFile.OpenRead(strPathZip))
            {
                foreach (ZipArchiveEntry entry in archive.Entries)
                    vstrFileNames.Add(entry.FullName);
            }

            return vstrFileNames;
        }

        public static bool IsFileExist(string strPathZip, string strFile)
        {
            using (ZipArchive archive = ZipFile.OpenRead(strPathZip))
            {
                ZipArchiveEntry entry = archive.Entries.Where(compFile => compFile.FullName == strFile).DefaultIfEmpty()?.First();

                if (entry != null)
                    return false;
            }
            return true;
        }

        public static MemoryStream OpenCompressWithoutUnzip(string strPathZip, string strFile)
        {
            Stream streamFile = null;

            using (ZipArchive archive = ZipFile.OpenRead(strPathZip))
            {
                ZipArchiveEntry entry = archive.Entries.Where(compFile => compFile.FullName == strFile).DefaultIfEmpty()?.First();

                if (entry != null)
                {
                    streamFile = entry.Open();

                    return CopyToMemory(streamFile);
                }
                else
                {

                }
            }
            return null;
        }
        public static MemoryStream OpenCompressWithoutUnzip(Stream streamZip, string strFile)
        {
            Stream streamFile = null;

            if (streamZip == null)
                return null;

            using (ZipArchive archive = new ZipArchive(streamZip))
            {
                ZipArchiveEntry entry = archive.Entries.Where(compFile => compFile.FullName == strFile).DefaultIfEmpty()?.First();

                if (entry != null)
                {
                    streamFile = entry.Open();

                    return CopyToMemory(streamFile);
                }
            }
            return null;
        }

        public static MemoryStream CopyToMemory(Stream input)
        {
            // It won't matter if we throw an exception during this method;
            // we don't *really* need to dispose of the MemoryStream, and the
            // caller should dispose of the input stream
            MemoryStream ret = new MemoryStream();

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ret.Write(buffer, 0, bytesRead);
            }
            // Rewind ready for reading (typical scenario)
            ret.Position = 0;
            return ret;
        }

        static public void ClearDirectory(string strFolderPath)
        {
            if (Directory.Exists(strFolderPath))
                Directory.Delete(strFolderPath, true);
        }

        static public void ClearFile(string strFilePath)
        {
            if (File.Exists(strFilePath))
                File.Delete(strFilePath);
        }

        static public void MoveFile(string source, string destination)
        {
            if (!File.Exists(source))
                return;

            if (!Directory.Exists(Path.GetDirectoryName(destination)))
                Directory.CreateDirectory(Path.GetDirectoryName(destination));

            // 2017.04.13 add M.S Ko
            if (File.Exists(destination))
            {
                File.Delete(destination);

                LogMessage.MessageOutput.ConsoleWrite("File was deleted & moved name of " + destination, ConsoleColor.White);
            }

            File.Move(source, destination);
        }
    }
}
