using CXMLDecompiler;
using General;
using Ionic.Zlib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace CXMLCli
{
    class Program
    {

        static string InputFilePath;
        static string OutputFilePath;
        static bool WaitExit = false;
        static bool ForcePs3 = false;
        static Endianness FileEndianness = Endianness.UNCHANGED;
        static bool Check = true;

        static int ArgIndex(string[] args, string argshort, string arglong)
        {
            for (int i = 0; i < args.Length; i++)
            {
                string arg = args[i];
                if (arg.ToLowerInvariant() == argshort.ToLowerInvariant() || arg.ToLowerInvariant() == arglong.ToLowerInvariant())
                {
                    return i;
                }
            }
            return -1;
        }
        static bool HasArg(string[] args, string argcheck)
        {
            foreach(string arg in args)
            {
                if(arg.ToLowerInvariant() == argcheck.ToLowerInvariant())
                {
                    return true;
                }
            }
            return false;
        }

        static string GetTablesFolder()
        {
            string tablesFolder = Path.GetFileNameWithoutExtension(OutputFilePath) + "_tables";
            if (!Directory.Exists(tablesFolder))
                Directory.CreateDirectory(tablesFolder);
            return tablesFolder;
        }
        static int Main(string[] args)
        {
	    
            // Check if the user has GimConv and download it if not present
            // (only do this on windows)
            
            bool windowsNT = Environment.OSVersion.Platform.Equals(PlatformID.Win32NT);
            bool windows32 = Environment.OSVersion.Platform.Equals(PlatformID.Win32Windows);
            bool windows32S = Environment.OSVersion.Platform.Equals(PlatformID.Win32S);
            bool windowsCE = Environment.OSVersion.Platform.Equals(PlatformID.WinCE);
            
            if (windowsNT || windows32 || windows32S || windowsCE)
            {
		        if (!GimConv.HasGimConv()) 
		        {
		            GimConv.DownloadGimConv(); // Politely ask sony to give it to us.
		        }
	        }
	        else
	        {
	    	    Console.WriteLine("NOTE: GimConv is windows binary (provided by Sony Computer Entertainment), so you cant use it on this OS!\n.GIM Decoding will be disabled!");
	        }


            if (args.Length == 0)
            {
                Console.WriteLine("-- CXML Decompiler --");
                Console.WriteLine("Required Arguments:");
                Console.WriteLine("\t-i            --input                Input file or folder");
                Console.WriteLine("\t-o            --output               Output file or folder");
                Console.WriteLine("Optional Arguments:");
                Console.WriteLine("\t-iv           --is-vag               File is HE-VAG audio and NOT CXML");
                Console.WriteLine("\t-dc           --decompress           Decompress .Z File");
                Console.WriteLine("\t-cc           --compress             Compress back to .Z File");
                Console.WriteLine("\t-f            --force                Dont check magic number.");
                Console.WriteLine("\t-dt           --dump-tables          Dump ALL tables.");
                Console.WriteLine("\t-tt           --dump-tree            Dump tree table.");
                Console.WriteLine("\t-idt          --dump-id              Dump id ID table.");
                Console.WriteLine("\t-iht          --dump-hash-id         Dump hash ID table.");
                Console.WriteLine("\t-st           --dump-string          Dump string table.");
                Console.WriteLine("\t-wt           --dump-wstring         Dump wstring table.");
                Console.WriteLine("\t-ht           --dump-hash            Dump hash table.");
                Console.WriteLine("\t-iat          --dump-int-array       Dump int array table.");
                Console.WriteLine("\t-fat          --dump-float-array     Dump float array table.");
                Console.WriteLine("\t-ft           --dump-file            Dump file table.");
                Console.WriteLine("\t-le           --little-endian        Decode ints as Little Endian");
                Console.WriteLine("\t-be           --big-endian           Decode ints as Big Endian");
                Console.WriteLine("\t-ps3          --playstation-3        Use the old CXML Format used back on PS3.");
                Console.WriteLine("\t-p            --process-files        Process Extracted Files");
                Console.WriteLine("\t-nr           --no-recursive         Do not recursively decompile .RCS to XML");
                Console.WriteLine("\t-w            --wait-exit            Wait for keypress before exiting");
                Console.WriteLine("\t-d            --decompile            Decompile CXML to XML.");
                Console.WriteLine("\t-c            --compile              Compile XML to CXML");
                Console.WriteLine("\t-s            --symbols              Use Symbol File (.rcd)");
                Console.WriteLine("Example Decompile: " + Path.GetFileName(Assembly.GetEntryAssembly().Location) + " -i common_resource.rco -o common_resource.xml -d -p");
                Console.WriteLine("Example Compile: " + Path.GetFileName(Assembly.GetEntryAssembly().Location) + " -i common_resource.xml -o common_resource.rco -c");
                return 0;
            }

            if (args.Length == 1)
            {
                List<string> argsCopy = args.ToList();

                argsCopy.AddRange(new string[] { "--input", args[0], "--output", Path.ChangeExtension(args[0], ".xml"), "--decompile", "--process-files", "--wait-exit" });

                if (File.Exists(Path.ChangeExtension(args[0], ".rcd")))
                    argsCopy.AddRange(new string[] { "--symbols", Path.ChangeExtension(args[0], ".rcd") });

                args = argsCopy.ToArray();
            }

            if(HasArg(args, "-i") || HasArg(args, "--input"))
            {
                int i = ArgIndex(args, "-i", "--input");
                InputFilePath = args[i + 1];
            }
            else
            {
                Console.Error.WriteLine("Missing required -i or --input argument.");
                return 1;
            }

            if (HasArg(args, "-o") || HasArg(args, "--output"))
            {
                int i = ArgIndex(args, "-o", "--output");
                OutputFilePath = args[i + 1];
            }
            else
            {
                Console.Error.WriteLine("Missing required -o or --output.");
                return 1;
            }

            if (HasArg(args, "-le") || HasArg(args, "--little-endian"))
            {
                FileEndianness = Endianness.LITTLE_ENDIAN;
            }

            if (HasArg(args, "-be") || HasArg(args, "--big-endian"))
            {
                FileEndianness = Endianness.LITTLE_ENDIAN;
            }

            if (HasArg(args, "-ps3") || HasArg(args, "--playstation-3"))
            {
                ForcePs3 = true;
            }
            
            if (HasArg(args, "-iv") || HasArg(args, "--is-vag"))
            {
                byte[] WaveData = VAG.VAGAudio.Vag2Wav(InputFilePath);
                Console.WriteLine("Writing "+ OutputFilePath);
                File.WriteAllBytes(OutputFilePath, WaveData);
                return 0;
            }

            if (HasArg(args, "-f") || HasArg(args, "--force"))
            {
                Check = false;
            }

            if (HasArg(args, "-dc") || HasArg(args, "--decompress"))
            {
                Console.WriteLine("Decompressing " + InputFilePath);
                Byte[] FileData = File.ReadAllBytes(InputFilePath);
                Byte[] DecompressedData = ZlibStream.UncompressBuffer(FileData);
                string Extension = Tools.GetFileExtension(DecompressedData);
                File.WriteAllBytes(OutputFilePath, DecompressedData);
                return 0;
            }

            if (HasArg(args, "-cc") || HasArg(args, "--compress"))
            {
                Console.WriteLine("Compressing " + InputFilePath);
                Byte[] FileData = File.ReadAllBytes(InputFilePath);
                Byte[] CompressedData = ZlibStream.CompressBuffer(FileData);
                string Extension = Tools.GetFileExtension(CompressedData);
                File.WriteAllBytes(OutputFilePath, CompressedData);
                return 0;
            }

            CXMLReader cxmlParser = new CXMLReader();
            if(!HasArg(args, "-c") && !HasArg(args, "--compile")) // If not Compiling
            {
                Console.WriteLine("Initalizing: " + InputFilePath);
                cxmlParser.Init(InputFilePath, OutputFilePath, Check, ForcePs3, FileEndianness);
            }
            
            if (HasArg(args, "-dt") || HasArg(args, "--dump-tables"))
            {
                List<string> argsCopy = args.ToList();

                argsCopy.AddRange(new string[] { "--dump-tree", "--dump-id", "--dump-hash-id", "--dump-string", "--dump-wstring", "--dump-hash", "--dump-int-array", "--dump-float-array", "--dump-file" });

                args = argsCopy.ToArray();
            }

            if (HasArg(args, "-tt") || HasArg(args, "--dump-tree"))
            {

                Console.WriteLine("Dumping tree table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "tree-table.bin"), cxmlParser.GetTreeTable());

            }

            if (HasArg(args, "-idt") || HasArg(args, "--dump-id"))
            {

                Console.WriteLine("Dumping ID table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "id-table.bin"), cxmlParser.GetIDTable());

            }

            if (HasArg(args, "-iht") || HasArg(args, "--dump-hash-id"))
            {
                Console.WriteLine("Dumping hash ID table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "hash-id-table.bin"), cxmlParser.GetHashIDTable());
            }

            if (HasArg(args, "-st") || HasArg(args, "--dump-string"))
            {
                Console.WriteLine("Dumping string table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "string-table.bin"),cxmlParser.GetStringTable());

            }

            if (HasArg(args, "-wt") || HasArg(args, "--dump-wstring"))
            {
                Console.WriteLine("Dumping wstring table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "wstring-table.bin"), cxmlParser.GetWStringTable());
            }

            if (HasArg(args, "-ht") || HasArg(args, "--dump-hash"))
            {
                Console.WriteLine("Dumping hash table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "hash-table.bin"), cxmlParser.GetHashTable());
            }


            if (HasArg(args, "-iat") || HasArg(args, "--dump-int-array"))
            {
                Console.WriteLine("Dumping int array table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "int-array-table.bin"), cxmlParser.GetIntArrayTable());
            }

            if (HasArg(args, "-fat") || HasArg(args, "--dump-float-array"))
            {
                Console.WriteLine("Dumping float array table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "float-array-table.bin"), cxmlParser.GetFloatArrayTable());
            }

            if (HasArg(args, "-ft") || HasArg(args, "--dump-tree"))
            {
                Console.WriteLine("Dumping file table.");
                File.WriteAllBytes(Path.Combine(GetTablesFolder(), "file-table.bin"), cxmlParser.GetFileTable());
            }

            if (HasArg(args, "-p") || HasArg(args, "--process-files"))
            {
                cxmlParser.ProcessFiles = true;
            }

            if (HasArg(args, "-w") || HasArg(args, "--wait-exit"))
            {
                WaitExit = true;
            }

            if (HasArg(args, "-nr") || HasArg(args, "--no-recursive"))
            {
                cxmlParser.NoRecursive = true;
            }

            if(HasArg(args, "-s") || HasArg(args, "--symbols"))
            {
                int i = ArgIndex(args, "-s", "--symbols");
                CXMLSymbols.Init(args[i + 1]);
            }

            if (HasArg(args, "-d") || HasArg(args, "--decompile") && !HasArg(args, "-dt"))
            {
                Console.WriteLine("Decompiling.");
                cxmlParser.DecompileCXML(InputFilePath, OutputFilePath);
                Console.WriteLine("\n\nDECOMPILATION COMPLETE!");
                if(WaitExit)
                    Console.ReadKey();
            }
            else if (HasArg(args, "-c") || HasArg(args, "--compile"))
            {
                Console.WriteLine("Compiling: " + InputFilePath);
                CXMLBuilder builder = new CXMLBuilder();

                builder.BuildCXML(InputFilePath, OutputFilePath);
                Console.WriteLine("\n\nCOMPILATION COMPLETE!");
                if (WaitExit)
                    Console.ReadKey();
            }

            return 0;
        }
    }
}
