using System.Text;

namespace MHiRepacker
{
    internal class Program
    {
        static string inputFile;
        static bool bUnpack;
        static string exceldata = "exceldata.dat";

        static int mode;

        static string helperStr = @"
————————————————————————
params:
[func] [inputpath] [outputpath]
func: unpack/repack
        unpack: [inputpath] is input File path ,[outputpath] is output Directory path
        repack: [inputpath] is input Directory path ,[outputpath] is output File path

eg.

unpack G:\exceldata.dat G:\exceldata_unpacked
repack G:\exceldata_unpacked G:\exceldata.dat

unpack G:\syokibugu.dat G:\syokibugu_unpacked
repack G:\syokibugu_unpacked G:\syokibugu
————————————————————————
";

        static void PrintHelperStr()
        {
            Console.WriteLine(helperStr);
            Console.ReadLine();
        }

        static void Main(string[] args)
        {

            string title = $"MHiRepacker Ver.1.4 By 皓月云 axibug.com";
            Console.Title = title;
            Console.WriteLine(title);

            if (args.Length < 3)
            {
                PrintHelperStr();
                return;
            }
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            Encoding shiftencode = Encoding.GetEncoding("Shift-JIS");

            string modecmd = args[0];

            Console.WriteLine($"func=>{args[0].ToLower()}");

            string inputpath;
            string outputpath;
            inputpath = args[1];
            outputpath = args[2];
            Console.WriteLine($"inputpath=>{inputpath}");
            Console.WriteLine($"outputpath=>{outputpath}");

            if (bUnpack)
            {
                if (!File.Exists(inputpath))
                {
                    Console.WriteLine($"未找到文件,can not found file: {inputpath}");
                    PrintHelperStr();
                    return;
                }
            }
            else if (bUnpack)
            {
                if (!Directory.Exists(inputpath))
                {
                    Console.WriteLine($"未找到文件,can not found directory: {inputpath}");
                    PrintHelperStr();
                    return;
                }
            }

            if (modecmd.ToLower() == "unpack")
            {
                bUnpack = true;
                if (!FileHelper.LoadFile(inputpath, out byte[] inputbytes))
                {
                    Console.WriteLine($"读取失败:{inputpath}");
                    PrintHelperStr();
                    return;
                }
                List<uint> SizeList = new List<uint>();
                List<string> tempLog = new List<string>();
                uint WorkedDataLenght = 0;
                if (!TryGetheadWithCheck(inputbytes, 0, false, ref SizeList, ref tempLog, out int checkmode, ref WorkedDataLenght))
                {
                    Console.WriteLine($"读取失败,文件以0x00起始,:{inputpath}");
                    PrintHelperStr();
                    return;
                }
                mode = checkmode;
            }
            else if (modecmd.ToLower() == "repack")
            {
                bUnpack = false;
                string[] dirs = Directory.GetDirectories(inputpath).Select(w => new DirectoryInfo(w).Name).ToArray();
                if (dirs.Length == 4 &&
                    dirs.Contains("0000") &&
                    dirs.Contains("0001") &&
                    dirs.Contains("0002") &&
                    dirs.Contains("0003")
                    )
                    mode = 1;
                else
                    mode = 0;
            }
            else
            {
                PrintHelperStr();
                return;
            }


            //if (args.Length >= 4 && args[3].ToLower() == "mode1")
            //    mode = 1;
            //else
            //    mode = 0;

            Console.WriteLine($"audo mode=>mode{mode}");

            bool bResult = false;
#if !DEBUG
                    try
                    {
#endif
            switch (mode)
            {
                case 0:
                    if (bUnpack) bResult = Unpack_Mode0(inputpath, outputpath);
                    else bResult = Repack_Mode0(inputpath, outputpath);
                    break;
                case 1:
                    if (bUnpack) bResult = Unpack_Mode1(inputpath, outputpath);
                    else bResult = Repack_Mode1(inputpath, outputpath);
                    break;
            }
#if !DEBUG
}
                    catch (Exception ex)
                    {
                        Console.WriteLine("处理异常:" + ex.ToString());
                    }
#endif
            Console.WriteLine(bResult ? "处理成功" : "处理失败");
        }

        static bool Unpack_Mode0(string Unpack_Input, string Unpack_Output)
        {
            Console.WriteLine($"开始处理{Unpack_Input}");
            if (!Directory.Exists(Unpack_Output))
            {
                Directory.CreateDirectory(Unpack_Output);
                Console.WriteLine($"创建目录{Unpack_Output}");
            }
            if (!FileHelper.LoadFile(Unpack_Input, out byte[] inputbytes))
            {
                Console.WriteLine($"读取失败");
                return false;
            }

            return Unpack_Mode0_logic(inputbytes, 0, Unpack_Output, out uint WorkedDataLenght);
        }

        readonly static uint StartHead = 0x01;
        readonly static byte[] StartPKHead = { 0x50, 0x4B, 0x03, 0x04, 0x14 };//PK
        static bool CheckPKHead(byte[] temp, int startIdx)
        {
            for (int i = 0; i < StartPKHead.Length; i++)
            {
                startIdx++;
                if (startIdx < temp.Length && StartPKHead[i] != temp[startIdx])
                    return false;
            }
            return true;
        }
        static bool TryGetheadWithCheck(byte[] inputbytes, uint startPos, bool skipConsoleLog, ref List<uint> SizeList, ref List<string> tempLog, out int checkmode, ref uint WorkedDataLenght)
        {
            if (inputbytes[0] == 0x00)
            {
                checkmode = default;
                return false;
            }
            WorkedDataLenght += StartHead;
            uint pos = startPos + StartHead;
            //bool CheckStartEnd(byte[] temp, int startIdx)
            //{
            //    for (int i = 0; i < StartHeadEndArr.Length; i++)
            //    {
            //        if (StartHeadEndArr[i] != temp[startIdx++])
            //            return true;
            //    }
            //    return false;
            //}

            //int idx = 0;
            uint StartContentPtr = 0;
            string log;

            log = $"文件头读取:";
            if (!skipConsoleLog) Console.WriteLine(log);
            tempLog.Add(log);

            //while (CheckStartEnd(inputbytes, (int)pos) && CheckStartEnd(inputbytes, (int)pos + 1))
            for (int idx = 0; idx < inputbytes[0]; idx++)
            {
                uint size = HexHelper.bytesToUInt(inputbytes, 2, (int)pos);
                SizeList.Add(size);
                log = $"lenght:[{idx}]=>{size}({size.ToString("X")})| byte src: 0x{inputbytes[pos].ToString("X")} 0x{inputbytes[pos + 1].ToString("X")}";
                if (!skipConsoleLog) Console.WriteLine(log);
                tempLog.Add(log);
                pos += 2;
                WorkedDataLenght += 2;
            }
            log = $"读取完毕共{SizeList.Count}个";
            if (!skipConsoleLog) Console.WriteLine(log);
            tempLog.Add(log);

            long packagelenght = WorkedDataLenght + SizeList.Sum(w => w);
            checkmode = inputbytes.Length == packagelenght ? 0 : 1;
            return true;
        }
        static bool Unpack_Mode0_logic(byte[] inputbytes, uint startPos, string Unpack_Output, out uint WorkedDataLenght)
        {
            WorkedDataLenght = 0;
            uint StartHead = 0x01;
            WorkedDataLenght += StartHead;
            //byte[] StartHeadEndArr = { 0x50, 0x4B, 0x03, 0x04, 0x14 };//PK

            List<string> tempLog = new List<string>();
            uint pos = startPos + StartHead;
            List<uint> SizeList = new List<uint>();
            try
            {
                uint StartContentPtr = 0;
                string log;
                tempLog.Add("文件头读取:");
                //while (CheckStartEnd(inputbytes, (int)pos) && CheckStartEnd(inputbytes, (int)pos + 1))
                for (int idx = 0; idx < inputbytes[0]; idx++)
                {
                    uint size = HexHelper.bytesToUInt(inputbytes, 2, (int)pos);
                    SizeList.Add(size);
                    log = $"lenght:[{idx}]=>{size}({size.ToString("X")})| byte src: 0x{inputbytes[pos].ToString("X")} 0x{inputbytes[pos + 1].ToString("X")}";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                    pos += 2;
                    WorkedDataLenght += 2;
                }
                log = $"读取完毕共{SizeList.Count}个";
                Console.WriteLine(log);
                tempLog.Add(log);

                StartContentPtr = pos;
                for (int i = 0; i < SizeList.Count; i++)
                {
                    string filename = Unpack_Output + $"\\{i.ToString().PadLeft(3, '0')}.zip";
                    using (FileStream fs = new FileStream(filename, FileMode.Create))
                    {
                        fs.Write(inputbytes, (int)StartContentPtr, (int)SizeList[i]);
                    }
                    StartContentPtr += SizeList[i];
                    WorkedDataLenght += SizeList[i];

                    log = $"写入{filename}";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                }


                log = $"Unpack完毕,共{SizeList.Count}个文件";
                Console.WriteLine(log);
                tempLog.Add(log);
                if (File.Exists(Unpack_Output + $"//filelist.txt"))
                    File.Delete(Unpack_Output + $"//filelist.txt");

                File.WriteAllLines(Unpack_Output + $"//filelist.txt", tempLog);

                return true;
            }
            catch (Exception e)
            {
                WorkedDataLenght = 0;
                return false;
            }
        }

        static bool Repack_Mode0(string Repack_Input, string Unpack_Output)
        {
            string outputdir = Path.GetDirectoryName(Unpack_Output);
            if (!Directory.Exists(outputdir))
            {
                Directory.CreateDirectory(outputdir);
                Console.WriteLine($"创建目录{outputdir}");
            }
            if (File.Exists(Unpack_Output + $".txt"))
                File.Delete(Unpack_Output + $".txt");
            List<string> tempLog = new List<string>();
            using (FileStream fs = new FileStream(Unpack_Output, FileMode.Create))
            {
                Repack_Mode0_logic(Repack_Input, fs, Unpack_Output, ref tempLog);
            }

            File.WriteAllLines(Unpack_Output + $".txt", tempLog);
            return true;
        }

        static bool Repack_Mode0_logic(string Repack_Input, FileStream fs, string Unpack_Output, ref List<string> tempLog, bool skip_y = false)
        {
            string[] zipfiles = FileHelper.GetDirFile(Repack_Input).Where(w => w.ToLower().EndsWith(".zip")).ToArray();
            List<byte[]> files = new List<byte[]>();

            Console.WriteLine($"-----------原数据读取完毕-----------");
            Console.WriteLine($"待处理文件:");
            for (int i = 0; i < zipfiles.Length; i++)
            {
                if (!FileHelper.LoadFile(zipfiles[i], out byte[] filedata))
                {
                    Console.WriteLine($"加载失败,文件:{zipfiles[i]}");
                    return false;
                }
                else
                {
                    files.Add(filedata);
                    Console.WriteLine($"加载,待处理文件:{zipfiles[i]},Done");
                }
            }

            if (!skip_y)
            {
                Console.WriteLine($"共{files.Count}个文件,是否处理? (y/n)");
                string yn = Console.ReadLine();
                if (yn.ToLower() != "y")
                    return false;
            }

            string log;
            fs.WriteByte((byte)files.Count);
            log = $"写入文件个数{files.Count}=>0x{files.Count.ToString("X")}";
            Console.WriteLine(log);
            tempLog.Add(log);

            for (int i = 0; i < files.Count; i++)
            {
                int lenght = files[i].Length;

                byte[] lenghtdata = HexHelper.uintToBytes(lenght);
                int lenghtVal = Math.Min(2, lenghtdata.Length);
                fs.Write(lenghtdata, 0, lenghtVal);
                if (lenghtdata.Length == 1)
                    fs.WriteByte(0);

                log = $"写入第[{i}]个文件大小{lenght}到文件头";
                Console.WriteLine(log);
                tempLog.Add(log);
            }

            log = $"文件头写入完毕";


            for (int i = 0; i < files.Count; i++)
            {
                fs.Write(files[i], 0, files[i].Length);
                log = $"写入第[{i}]个文件";
                Console.WriteLine(log);
                tempLog.Add(log);
            }


            log = $"Repack完毕:{Unpack_Output}";
            Console.WriteLine(log);
            tempLog.Add(log);

            return true;
        }

        static bool Unpack_Mode1(string Unpack_Input, string Unpack_Output)
        {
            Console.WriteLine($"开始处理{Unpack_Input}");

            if (!Directory.Exists(Unpack_Output))
            {
                Directory.CreateDirectory(Unpack_Output);
                Console.WriteLine($"创建目录{Unpack_Output}");
            }


            if (!FileHelper.LoadFile(Unpack_Input, out byte[] fulldata))
            {
                Console.WriteLine($"读取失败");
                return false;
            }

            int subPackIdx = 0;
            uint Pos = 0;
            while (Pos < fulldata.Length)
            {
                Console.WriteLine($"----- 开始处理:SubPack[{subPackIdx}] -----");
                string SubPackOutDir = Path.Combine(Unpack_Output, subPackIdx.ToString().PadLeft(4, '0'));
                if (!Directory.Exists(SubPackOutDir))
                {
                    Directory.CreateDirectory(SubPackOutDir);
                    Console.WriteLine($"创建目录{SubPackOutDir}");
                }

                if (!Unpack_Mode0_logic(fulldata, Pos, SubPackOutDir, out uint WorkedDataLenght))
                {
                    Console.WriteLine($"SubPack[{subPackIdx}],处理失败");
                    return false;
                }
                Pos += WorkedDataLenght;

                //处理非0x00
                Console.WriteLine($"----- 处理:SubPack[{subPackIdx}]后续非0x00数据 -----");
                List<byte> temp_Not0 = new List<byte>();
                while (true)
                {
                    if (Pos + 1 < fulldata.Length && fulldata[Pos + 1] != 0x00)
                    {
                        Pos += 1;
                        temp_Not0.Add(fulldata[Pos]);
                    }
                    else
                    {
                        break;
                    }
                }

                string tail_not0 = Path.Combine(SubPackOutDir, "tail_not0");
                byte[] read_data_not0 = temp_Not0.ToArray();
                using (FileStream fs = new FileStream(tail_not0, FileMode.Create))
                {
                    fs.Write(read_data_not0, 0, read_data_not0.Length);
                }
                Console.WriteLine($"----- SubPack[{subPackIdx}]后续非0x00数据,Length:{read_data_not0.Length}存储到{tail_not0} -----");


                //处理0x00
                Console.WriteLine($"----- 处理:SubPack[{subPackIdx}]后续0x00数据 -----");
                List<byte> temp_0 = new List<byte>();
                while (true)
                {
                    if (Pos + 1 < fulldata.Length && fulldata[Pos + 1] == 0x00)
                    {
                        Pos += 1;
                        temp_0.Add(fulldata[Pos]);
                    }
                    else
                    {
                        break;
                    }
                }

                string tail_0 = Path.Combine(SubPackOutDir, "tail");
                byte[] read_data_0 = temp_0.ToArray();
                using (FileStream fs = new FileStream(tail_0, FileMode.Create))
                {
                    fs.Write(read_data_0, 0, read_data_0.Length);
                }

                Console.WriteLine($"----- SubPack[{subPackIdx}]后续0x00数据,Length:{read_data_0.Length}存储到{tail_0} -----");

                Console.WriteLine($"----- 处理完毕:SubPack[{subPackIdx}] -----");
                subPackIdx++;
                Pos += 1;
            }

            return true;

        }

        readonly static int[] subpacklenght = { 14336, 7600, 1500, 28400 };

        static bool Repack_Mode1(string Repack_Input, string Repack_Output)
        {
            string outputdir = Path.GetDirectoryName(Repack_Output);
            if (!Directory.Exists(outputdir))
            {
                Directory.CreateDirectory(outputdir);
                Console.WriteLine($"创建目录{outputdir}");
            }

            string[] dirs = Directory.GetDirectories(Repack_Input);
            for (int i = 0; i < dirs.Length; i++)
                Console.WriteLine($"发现目录:{dirs[i]}");

            Console.WriteLine($"共{dirs.Length}个目录,是否处理? (y/n)");
            string yn = Console.ReadLine();
            if (yn.ToLower() != "y")
                return false;

            List<string> tempLog = new List<string>();
            string log;
            if (File.Exists(Repack_Output + $".txt"))
                File.Delete(Repack_Output + $".txt");

            using (FileStream fs = new FileStream(Repack_Output, FileMode.Create))
            {
                for (int subpack_idx = 0; subpack_idx < dirs.Length; subpack_idx++)
                {
                    string subdir = dirs[subpack_idx];

                    log = $"-- 开始写入Subpack[{subpack_idx}]:下的zip包";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                    Repack_Mode0_logic(subdir, fs, Repack_Output, ref tempLog, true);
                    log = $"-- 完成写入Subpack[{subpack_idx}]:下的zip包";
                    Console.WriteLine(log);
                    tempLog.Add(log);

                    string tail_not0 = Path.Combine(subdir, "tail_not0");
                    log = $"-- 开始写入Subpack[{subpack_idx}]:下的非0x00数据:{tail_not0}";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                    if (!FileHelper.LoadFile(tail_not0, out byte[] inputbytes_not0))
                    {
                        Console.WriteLine($"读取失败");
                        return false;
                    }
                    fs.Write(inputbytes_not0, 0, inputbytes_not0.Length);
                    log = $"-- 完成写入Subpack[{subpack_idx}]:下的非0x00数据:length{inputbytes_not0.Length}";
                    Console.WriteLine(log);
                    tempLog.Add(log);


                    int targetSize = 0;
                    for (int sizeidx = 0; sizeidx <= subpack_idx; sizeidx++)
                        targetSize += subpacklenght[sizeidx];

                    long needAdd = (targetSize - fs.Length);

                    log = $"-- 需要补0x00长度:{needAdd}";
                    Console.WriteLine(log);
                    tempLog.Add(log);

                    if (needAdd < 0)
                    {
                        Console.WriteLine($"--Subpack[{subpack_idx}],超出MHi预设区间大小");
                        return false;
                    }

                    log = $"-- 开始写入Subpack[{subpack_idx}]:下的0x00数据";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                    for (int i = 0; i < needAdd; i++)
                        fs.WriteByte(0x00);
                    log = $"-- 完成写入Subpack[{subpack_idx}]:下的0x00数据:length{needAdd}";
                    Console.WriteLine(log);
                    tempLog.Add(log);
                }
            }
            File.WriteAllLines(Repack_Output + $".txt", tempLog);
            return true;
        }

    }
}