using Microsoft.AspNetCore.Mvc;
using SharpPdb.Native;

using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.IO;
using Microsoft.AspNetCore.StaticFiles;
using System.Runtime.InteropServices;

using Zodiacon.DebugHelp;

using Microsoft.PdbDownloader.Logic.Pdb;
using Microsoft.PdbDownloader.Logic.Pe;

namespace PdbServer.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class Symbols : ControllerBase
    {
        [DllImport("Symbol Parser.dll",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int EnumFunction(IntPtr pdbpath,IntPtr fname);

        //const string pdburl = "http://msdl.microsoft.com/download/";
        const string pdburl = "http://msdl.blackint3.com:88/download/";
        const string selfdocpath = "D:\\Web\\Symbols";
        private readonly ILogger<Symbols> _logger;
        public Symbols(ILogger<Symbols> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        [Route("QueryAllModuleSymbol")]
        public string QueryAllModuleSymbol(string ModuleName, string Guid, string Agent)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }

            if (System.IO.File.Exists(filename))
            {
                using var reader = new PdbFileReader(filename);

                foreach (var v in reader.Functions)
                {
                    sb.Append(v.Offset);
                    sb.Append('#');
                    sb.Append(v.Name);
                    sb.Append('|');
                }
            }
            if (sb.Length > 0) sb.Length -= 1;
            return sb.ToString();
        }
        [HttpGet]
        [Route("SymbolCheck")]
        public int SymbolCheck(string ModuleName, string Guid, string Agent)
        {
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if(System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return 0;
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
                else
                {
                    System.IO.File.WriteAllBytes($"{selfdocpath}\\{Guid}_{Agent}.no", new byte[0]);
                }
            }

            if (System.IO.File.Exists(filename))
            {
                return 1;
            }
            return 0;
        }
        public static byte[] Compress(byte[] rawData)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            System.IO.Compression.GZipStream compressedzipStream = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true);
            compressedzipStream.Write(rawData, 0, rawData.Length);
            compressedzipStream.Close();
            return ms.ToArray();
        }
        [HttpGet]
        [Route("SymbolDownLoad")]
        public string SymbolDownLoad(string ModuleName, string Guid, string Agent)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return "";
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                byte[] filebuffer = System.IO.File.ReadAllBytes(filename);
                var bytes= Compress(filebuffer);
                for(int i=0;i<bytes.Length; i++)
                {
                    sb.Append(bytes[i].ToString("X2"));
                }
            }
            return sb.ToString();
        }

        [HttpGet]
        [Route("QueryModuleSymbol")]//https://localhost:7001/Symbols/QueryModuleSymbol?ModuleName=dwmcore.pdb&Guid=47E4AD6A46A936FC873379E0D86E94FC&Agent=1&QueryName=CLegacySwapChain::Present
        public Int64 QueryModuleSymbol(string ModuleName, string Guid, string Agent, string QueryName)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return -1;
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                var offst = EnumFunction(Marshal.StringToHGlobalAnsi(filename), Marshal.StringToHGlobalAnsi(QueryName));
                return offst;
                //SymbolHandler handler = SymbolHandler.Create();
                //ulong ptr = 0;
                //ptr = handler.TryLoadSymbolsForModuleAsync(filename, 0x1000000UL).Result;
                //foreach (var v in handler.EnumSymbols(ptr))
                //{
                //    if (v.Name.Trim().ToLower() == QueryName.ToLower())
                //    {
                //        handler.Dispose();
                //        return (Int64)(v.Address - 0x1000000UL);
                //    }
                //}
                //handler.Dispose();
            }
            return -1;
        }
        [HttpGet]
        [Route("QueryModuleSymbolEX00A")]//https://localhost:7001/Symbols/QueryModuleSymbol?ModuleName=dwmcore.pdb&Guid=47E4AD6A46A936FC873379E0D86E94FC&Agent=1&QueryName=CLegacySwapChain::Present
        public Int64 QueryModuleSymbolEX00A(string ModuleName, string Guid, string Agent, string QueryName)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return -1;
                }Marshal.StringToHGlobalAnsi(filename);
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                var offst = EnumFunction(Marshal.StringToHGlobalAnsi(filename), Marshal.StringToHGlobalAnsi(QueryName));
                return offst;
                //SymbolHandler handler = SymbolHandler.Create();
                //ulong ptr = 0;
                //ptr = handler.TryLoadSymbolsForModuleAsync(filename, 0x1000000UL).Result;
                //foreach (var v in handler.EnumSymbols(ptr))
                //{
                //    if (v.Name.Trim().ToLower() == QueryName.ToLower())
                //    {
                //        handler.Dispose();
                //        return (Int64)(v.Address - 0x1000000UL);
                //    }
                //}
                //handler.Dispose();
            }
            return -1;
        }
        //https://localhost:7001/Symbols/QueryModuleStruct?ModuleName=ntkrnlmp.pdb&Guid=F55005B80C686C739E763E6CF2559D9F&Agent=1&StructName=_EPROCESS&FildName=ActiveProcessLinks
        [HttpGet]
        [Route("QueryModuleStructFildOffset")]
        public Int64 QueryModuleStructFildOffset(string ModuleName, string Guid, string Agent, string StructName, string FildName)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return -1;
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                using var reader = new PdbFileReader(filename);
                foreach (var v in reader.UserDefinedTypes)
                {
                    if (v is SharpPdb.Native.Types.PdbClassType _struct)
                    {
                        if (_struct.Name == StructName)
                        {
                            foreach (var fild in _struct.Fields)
                            {
                                if (fild.Name == FildName)
                                {
                                    return (Int64)fild.Offset;
                                }
                            }
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbUnionType _struct1)
                    {
                        if (_struct1.Name == StructName)
                        {
                            foreach (var fild in _struct1.Fields)
                            {
                                if (fild.Name == FildName)
                                {
                                    return (Int64)fild.Offset;
                                }
                            }
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbEnumType _enum)
                    {
                        if (_enum.Name == StructName)
                        {
                            foreach (var val in _enum.Values)
                            {
                                if (val.Name == FildName)
                                {
                                    return (Int64)val.Value;
                                }
                            }
                        }
                    }
                }
            }
            return -1;
        }
        [HttpGet]
        [Route("QueryModuleStructFilds")]
        public string QueryModuleStructFilds(string ModuleName, string Guid, string Agent, string StructName)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return sb.ToString();
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                using var reader = new PdbFileReader(filename);
                foreach (var v in reader.UserDefinedTypes)
                {
                    if (v is SharpPdb.Native.Types.PdbClassType _struct)
                    {
                        if (_struct.Name == StructName)
                        {
                            foreach (var fild in _struct.Fields)
                            {
                                sb.Append($"{fild.Name},{(Int64)fild.Offset};");
                            }
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbUnionType _struct1)
                    {
                        if (_struct1.Name == StructName)
                        {
                            foreach (var fild in _struct1.Fields)
                            {
                                sb.Append($"{fild.Name},{(Int64)fild.Offset};");
                            }
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbEnumType _enum)
                    {
                        if (_enum.Name == StructName)
                        {
                            foreach (var val in _enum.Values)
                            {
                                sb.Append($"{val.Name},{val.Value};");
                            }
                        }
                    }
                }
            }
            return sb.ToString();
        }
        //http://www.sym101.com/Symbols/QueryModuleStruct?ModuleName=ntkrnlmp.pdb&Guid=F55005B80C686C739E763E6CF2559D9F&Agent=1
        [HttpGet]
        [Route("QueryModuleStruct")]
        public string QueryModuleStruct(string ModuleName, string Guid, string Agent)
        {
            StringBuilder sb = new StringBuilder();
            string filename = $"{selfdocpath}\\{ModuleName}_{Guid}_{Agent}.pdb";
            if (!System.IO.File.Exists(filename))
            {
                if (System.IO.File.Exists($"{selfdocpath}\\{Guid}_{Agent}.no"))
                {
                    return sb.ToString();
                }
                byte[] data = Helpers.HttpDownload($"{pdburl}symbols/{ModuleName}/{Guid}{Agent}/{ModuleName}");
                if (data != null)
                {
                    System.IO.File.WriteAllBytes(filename, data);
                }
            }
            if (System.IO.File.Exists(filename))
            {
                using var reader = new PdbFileReader(filename);
                foreach (var v in reader.UserDefinedTypes)
                {
                    if (v is SharpPdb.Native.Types.PdbClassType _struct)
                    {
                        sb.Append($"{v.Name}@");
                        foreach (var fild in _struct.Fields)
                        {
                            sb.Append($"{fild.Name},{(Int64)fild.Offset};");
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbUnionType _struct1)
                    {
                        sb.Append($"{v.Name}@");
                        foreach (var fild in _struct1.Fields)
                        {
                            sb.Append($"{fild.Name},{(Int64)fild.Offset};");
                        }
                    }
                    else if (v is SharpPdb.Native.Types.PdbEnumType _enum)
                    {
                        sb.Append($"{v.Name}@");
                        foreach (var val in _enum.Values)
                        {             
                            sb.Append($"{val.Name},{val.Value};");
                        }
                    }
                }
            }
            return sb.ToString();
        }

    }
}
