

namespace MemoryViewer
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Numerics;
    using System.Text;
    using GameHelper;
    using GameHelper.Plugin;
    using GameHelper.Utils;
    using GameOffsets;
    using ImGuiNET;
    using Newtonsoft.Json;

    
    public sealed class MemoryViewer : PCore<MemoryViewerSettings>
    {
        private bool isWindowVisible = false;
        private DateTime lastRefreshTime = DateTime.MinValue;
        private MemoryValidationData validationData = new MemoryValidationData();

        private struct MemoryValidationData
        {
            public bool GameStatesValid;
            public IntPtr GameStatesAddress;
            public IntPtr InGameStateAddress;
            public IntPtr AreaInstanceAddress;
            public IntPtr IngameDataAddress;
            public IntPtr EntityListAddress;
            public IntPtr LocalPlayerAddress;
            public Dictionary<string, AddressInfo> Addresses;
        }

        private struct AddressInfo
        {
            public string Name;
            public IntPtr Address;
            public bool IsValid;
            public bool IsReadable;
            public string ValueHex;
            public string Description;
        }

        
        public override void OnEnable(bool isGameOpened)
        {
            LoadSettings();
        }

        
        public override void OnDisable()
        {
            SaveSettings();
        }

        
        public override void SaveSettings()
        {
            try
            {
                var settingsPath = Path.Combine(this.DllDirectory, "settings.json");
                var json = JsonConvert.SerializeObject(this.Settings, Formatting.Indented);
                File.WriteAllText(settingsPath, json);
            }
            catch (Exception ex)
            {
                CategorizedLogger.LogError($"[MemoryViewer] Failed to save settings: {ex.Message}");
            }
        }

        private void LoadSettings()
        {
            try
            {
                var settingsPath = Path.Combine(this.DllDirectory, "settings.json");
                if (File.Exists(settingsPath))
                {
                    var json = File.ReadAllText(settingsPath);
                    this.Settings = JsonConvert.DeserializeObject<MemoryViewerSettings>(json) ?? new MemoryViewerSettings();
                }
            }
            catch (Exception ex)
            {
                CategorizedLogger.LogError($"[MemoryViewer] Failed to load settings: {ex.Message}");
                this.Settings = new MemoryViewerSettings();
            }
        }

        
        public override void DrawSettings()
        {
            if (ImGui.CollapsingHeader("Memory Viewer Settings"))
            {
                var hexBytes = this.Settings.HexViewBytes;
                if (ImGui.InputInt("Hex View Bytes", ref hexBytes, 64, 256))
                {
                    this.Settings.HexViewBytes = Math.Max(64, Math.Min(4096, hexBytes));
                }

                ImGui.Checkbox("Auto Refresh", ref this.Settings.AutoRefresh);

                if (this.Settings.AutoRefresh)
                {
                    var interval = this.Settings.RefreshIntervalMs;
                    if (ImGui.InputInt("Refresh Interval (ms)", ref interval, 100, 500))
                    {
                        this.Settings.RefreshIntervalMs = Math.Max(100, Math.Min(10000, interval));
                    }
                }
            }
        }

        
        public override void DrawUI()
        {
            if (Core.Process.Handle == null || Core.Process.Handle.IsInvalid || Core.Process.Handle.IsClosed)
            {
                return;
            }

            
            if (this.Settings.AutoRefresh && 
                (DateTime.Now - lastRefreshTime).TotalMilliseconds >= this.Settings.RefreshIntervalMs)
            {
                RefreshMemoryData();
                lastRefreshTime = DateTime.Now;
            }

            ImGui.SetNextWindowSize(new Vector2(900, 700), ImGuiCond.FirstUseEver);
            if (ImGui.Begin("Memory Viewer", ref this.isWindowVisible))
            {
                DrawMemoryViewer();
                ImGui.End();
            }
        }

        private void DrawMemoryViewer()
        {
            ImGui.TextWrapped("Memory Address Validator - View and validate game memory addresses");
            ImGui.Separator();

            if (ImGui.Button("Refresh Data"))
            {
                RefreshMemoryData();
            }

            ImGui.SameLine();
            if (ImGui.Button("Dump to Log"))
            {
                DumpToLog();
            }

            ImGui.Separator();

            
            if (ImGui.CollapsingHeader("GameStates Structure"))
            {
                DrawAddressInfo("GameStates", validationData.GameStatesAddress, validationData.GameStatesValid,
                    "Main GameStates pointer - Base offset for all game state access");
                
                if (validationData.GameStatesValid && validationData.GameStatesAddress != IntPtr.Zero)
                {
                    DrawOffsetInfo("InGameState Index", 4, "Index in GameStates array");
                }
            }

            
            if (ImGui.CollapsingHeader("InGameState Structure"))
            {
                DrawAddressInfo("InGameState", validationData.InGameStateAddress, validationData.InGameStateAddress != IntPtr.Zero,
                    "Current InGameState pointer");
                
                if (validationData.InGameStateAddress != IntPtr.Zero)
                {
                    DrawOffsetInfo("AreaInstanceData", 0x948, "AreaInstanceData offset");
                    DrawOffsetInfo("IngameData Offset", 0x370, "IngameData structure offset");
                }
            }

            
            if (ImGui.CollapsingHeader("IngameData Structure"))
            {
                DrawAddressInfo("IngameData", validationData.IngameDataAddress, validationData.IngameDataAddress != IntPtr.Zero,
                    "IngameData structure pointer");
                
                if (validationData.IngameDataAddress != IntPtr.Zero)
                {
                    DrawOffsetInfo("Entity List", 0x490, "EntityList pointer offset");
                    DrawOffsetInfo("Entities Count", 0x498, "Entity count offset");
                    DrawOffsetInfo("Local Player", 0x408, "Local player pointer offset");
                }
            }

            
            if (ImGui.CollapsingHeader("AreaInstance Structure"))
            {
                DrawAddressInfo("AreaInstance", validationData.AreaInstanceAddress, validationData.AreaInstanceAddress != IntPtr.Zero,
                    "AreaInstance structure pointer");
                
                if (validationData.AreaInstanceAddress != IntPtr.Zero)
                {
                    DrawOffsetInfo("Local Player Ptr", 0xA10, "Local player pointer");
                    DrawOffsetInfo("Entity List Ptr", 0xB50, "Entity list pointer (was 0x13F8)");
                    DrawOffsetInfo("Terrain List Ptr", 0x12C8, "Terrain/Exits list pointer");
                    DrawOffsetInfo("Terrain Grid Ptr", 0x08, "Terrain grid pointer (was 0x30)");
                    DrawOffsetInfo("Terrain Dimensions", 0x28, "Terrain dimensions pointer");
                }
            }

            
            if (ImGui.CollapsingHeader("Entity Structure"))
            {
                DrawOffsetInfo("Component List Ptr", 0x10, "ComponentList pointer (inside ItemBase)");
            }

            
            if (ImGui.CollapsingHeader("Component Offsets"))
            {
                DrawOffsetInfo("Component Index Debuffs", 3, "Debuff component index");
                DrawOffsetInfo("Component Owner Entity", 0x08, "Entity pointer in ComponentHeader");
            }

            
            if (ImGui.CollapsingHeader("Life Component Offsets"))
            {
                DrawOffsetInfo("Health", 0x1A8, "Health VitalStruct offset");
                DrawOffsetInfo("Mana", 0x1F8, "Mana VitalStruct offset");
                DrawOffsetInfo("Energy Shield", 0x230, "Energy Shield VitalStruct offset");
                DrawOffsetInfo("Buffs", 0x58, "Buffs offset");
            }

            
            if (ImGui.CollapsingHeader("VitalStruct Offsets"))
            {
                DrawOffsetInfo("Reserved Flat", 0x10, "Reserved flat value");
                DrawOffsetInfo("Reserved Percent", 0x14, "Reserved percentage value");
                DrawOffsetInfo("Total (Max)", 0x2C, "Maximum value");
                DrawOffsetInfo("Current", 0x30, "Current value");
            }

            
            if (ImGui.CollapsingHeader("Render/Position Component"))
            {
                DrawOffsetInfo("Position X", 0x138, "X coordinate");
                DrawOffsetInfo("Position Y", 0x13C, "Y coordinate");
                DrawOffsetInfo("Position Z", 0x140, "Z coordinate");
            }

            
            if (ImGui.CollapsingHeader("Memory Hex View"))
            {
                if (validationData.Addresses != null && validationData.Addresses.Count > 0)
                {
                    var selectedAddr = validationData.Addresses.First().Key;
                    if (ImGui.BeginCombo("Select Address", selectedAddr))
                    {
                        foreach (var addr in validationData.Addresses.Keys)
                        {
                            bool isSelected = addr == selectedAddr;
                            if (ImGui.Selectable(addr, isSelected))
                            {
                                selectedAddr = addr;
                            }
                            if (isSelected)
                            {
                                ImGui.SetItemDefaultFocus();
                            }
                        }
                        ImGui.EndCombo();
                    }

                    if (validationData.Addresses.ContainsKey(selectedAddr))
                    {
                        var addrInfo = validationData.Addresses[selectedAddr];
                        DrawHexView(addrInfo.Address, this.Settings.HexViewBytes);
                    }
                }
            }
        }

        private void DrawAddressInfo(string name, IntPtr address, bool isValid, string description)
        {
            var color = isValid && address != IntPtr.Zero ? new Vector4(0, 1, 0, 1) : new Vector4(1, 0, 0, 1);
            var status = isValid && address != IntPtr.Zero ? "✓ VALID" : "✗ INVALID";
            
            ImGui.TextColored(color, $"{status} {name}");
            ImGui.SameLine();
            ImGui.Text($"0x{address.ToInt64():X}");
            if (!string.IsNullOrEmpty(description))
            {
                ImGui.TextWrapped($"  {description}");
            }
        }

        private void DrawOffsetInfo(string name, long offset, string description)
        {
            ImGui.Text($"{name}: 0x{offset:X}");
            if (!string.IsNullOrEmpty(description))
            {
                ImGui.SameLine();
                ImGui.TextDisabled($"({description})");
            }
        }

        private void DrawHexView(IntPtr address, int byteCount)
        {
            if (address == IntPtr.Zero)
            {
                ImGui.Text("Invalid address");
                return;
            }

            try
            {
                var reader = Core.Process.Handle;
                var bytes = reader.ReadMemoryArray<byte>(address, byteCount);
                
                if (bytes != null && bytes.Length > 0)
                {
                    ImGui.Text($"Address: 0x{address.ToInt64():X} ({bytes.Length} bytes)");
                    ImGui.Separator();
                    
                    ImGui.PushFont(ImGui.GetIO().Fonts.Fonts[1]); 
                    var sb = new StringBuilder();
                    
                    for (int i = 0; i < bytes.Length; i++)
                    {
                        if (i > 0 && i % 16 == 0)
                        {
                            ImGui.Text(sb.ToString());
                            sb.Clear();
                        }
                        sb.Append($"{bytes[i]:X2} ");
                    }
                    if (sb.Length > 0)
                    {
                        ImGui.Text(sb.ToString());
                    }
                    
                    ImGui.PopFont();
                }
            }
            catch (Exception ex)
            {
                ImGui.TextColored(new Vector4(1, 0, 0, 1), $"Error reading memory: {ex.Message}");
            }
        }

        private void RefreshMemoryData()
        {
            validationData = new MemoryValidationData
            {
                Addresses = new Dictionary<string, AddressInfo>()
            };

            if (Core.Process.Handle == null || Core.Process.Handle.IsInvalid || Core.Process.Handle.IsClosed)
            {
                return;
            }

            try
            {
                var reader = Core.Process.Handle;

                
                if (Core.Process.StaticAddresses.ContainsKey("Game States"))
                {
                    validationData.GameStatesAddress = Core.Process.StaticAddresses["Game States"];
                    validationData.GameStatesValid = ValidateAddress(validationData.GameStatesAddress);
                    
                    if (validationData.GameStatesValid)
                    {
                        
                        try
                        {
                            var gameStatesPtr = reader.ReadMemory<IntPtr>(validationData.GameStatesAddress);
                            var inGameStatePtr = reader.ReadMemory<IntPtr>(new IntPtr(gameStatesPtr.ToInt64() + (4 * 8))); 
                            
                            if (ValidateAddress(inGameStatePtr))
                            {
                                validationData.InGameStateAddress = inGameStatePtr;
                                
                                
                                try
                                {
                                    var ingameDataPtr = reader.ReadMemory<IntPtr>(new IntPtr(inGameStatePtr.ToInt64() + 0x370));
                                    if (ValidateAddress(ingameDataPtr))
                                    {
                                        validationData.IngameDataAddress = ingameDataPtr;
                                        
                                        
                                        try
                                        {
                                            var localPlayerPtr = reader.ReadMemory<IntPtr>(new IntPtr(ingameDataPtr.ToInt64() + 0x408));
                                            if (ValidateAddress(localPlayerPtr))
                                            {
                                                validationData.LocalPlayerAddress = localPlayerPtr;
                                                validationData.Addresses["Local Player"] = new AddressInfo
                                                {
                                                    Name = "Local Player",
                                                    Address = localPlayerPtr,
                                                    IsValid = true,
                                                    IsReadable = true,
                                                    ValueHex = $"0x{localPlayerPtr.ToInt64():X}",
                                                    Description = "Local player entity pointer"
                                                };
                                            }
                                        }
                                        catch { }
                                    }
                                }
                                catch { }
                            }
                        }
                        catch { }
                    }
                }

                
                if (validationData.GameStatesAddress != IntPtr.Zero)
                {
                    validationData.Addresses["GameStates"] = new AddressInfo
                    {
                        Name = "GameStates",
                        Address = validationData.GameStatesAddress,
                        IsValid = validationData.GameStatesValid,
                        IsReadable = validationData.GameStatesValid,
                        ValueHex = $"0x{validationData.GameStatesAddress.ToInt64():X}",
                        Description = "GameStates pointer"
                    };
                }

                if (validationData.InGameStateAddress != IntPtr.Zero)
                {
                    validationData.Addresses["InGameState"] = new AddressInfo
                    {
                        Name = "InGameState",
                        Address = validationData.InGameStateAddress,
                        IsValid = true,
                        IsReadable = true,
                        ValueHex = $"0x{validationData.InGameStateAddress.ToInt64():X}",
                        Description = "InGameState pointer"
                    };
                }

                if (validationData.IngameDataAddress != IntPtr.Zero)
                {
                    validationData.Addresses["IngameData"] = new AddressInfo
                    {
                        Name = "IngameData",
                        Address = validationData.IngameDataAddress,
                        IsValid = true,
                        IsReadable = true,
                        ValueHex = $"0x{validationData.IngameDataAddress.ToInt64():X}",
                        Description = "IngameData pointer"
                    };
                }
            }
            catch (Exception ex)
            {
                CategorizedLogger.LogError($"[MemoryViewer] Error refreshing memory data: {ex.Message}");
            }
        }

        private bool ValidateAddress(IntPtr address)
        {
            if (address == IntPtr.Zero || address.ToInt64() < 0x10000 || address.ToInt64() > 0x7FFFFFFFFFFF)
            {
                return false;
            }

            try
            {
                var reader = Core.Process.Handle;
                
                reader.ReadMemory<byte>(address);
                return true;
            }
            catch
            {
                return false;
            }
        }

        private void DumpToLog()
        {
            var sb = new StringBuilder();
            sb.AppendLine("=== Memory Viewer Dump ===");
            sb.AppendLine($"GameStates: 0x{validationData.GameStatesAddress.ToInt64():X} (Valid: {validationData.GameStatesValid})");
            sb.AppendLine($"InGameState: 0x{validationData.InGameStateAddress.ToInt64():X}");
            sb.AppendLine($"IngameData: 0x{validationData.IngameDataAddress.ToInt64():X}");
            sb.AppendLine($"Local Player: 0x{validationData.LocalPlayerAddress.ToInt64():X}");
            sb.AppendLine();
            sb.AppendLine("All Addresses:");
            foreach (var addr in validationData.Addresses.Values)
            {
                sb.AppendLine($"  {addr.Name}: 0x{addr.Address.ToInt64():X} (Valid: {addr.IsValid}, Readable: {addr.IsReadable})");
            }

            CategorizedLogger.Log(CategorizedLogger.LogCategory.AddressOffsets, sb.ToString());
        }
    }
}
