

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

    
    public sealed class PatternTool : PCore<PatternToolSettings>
    {
        private string _dumpDirectory;
        private string _customPatternInput = string.Empty;
        private string _lastScanResult = string.Empty;
        private readonly List<PatternScanResult> _scanResults = new();
        private readonly Dictionary<string, OffsetInfo> _offsetInfo = new();

        private struct PatternScanResult
        {
            public string PatternName;
            public IntPtr Address;
            public long Offset;
            public string PatternString;
            public bool Valid;
        }

        private struct OffsetInfo
        {
            public string Name;
            public long Offset;
            public string Description;
            public IntPtr? Value;
        }

        
        public override void OnEnable(bool isGameOpened)
        {
            _dumpDirectory = Path.Combine(this.DllDirectory, this.Settings.OutputDirectory);
            Directory.CreateDirectory(_dumpDirectory);
            
            LoadSettings();
            InitializeOffsetInfo();
        }

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

        
        public override void DrawSettings()
        {
            if (ImGui.CollapsingHeader("Pattern Tool Settings"))
            {
                var outputDir = this.Settings.OutputDirectory;
                if (ImGui.InputText("Output Directory", ref outputDir, 256))
                {
                    this.Settings.OutputDirectory = outputDir;
                    _dumpDirectory = Path.Combine(this.DllDirectory, this.Settings.OutputDirectory);
                    Directory.CreateDirectory(_dumpDirectory);
                }

                ImGui.Checkbox("Auto Dump on Area Change", ref this.Settings.AutoDumpOnAreaChange);
                ImGui.Checkbox("Enable Custom Pattern Scan", ref this.Settings.EnableCustomPatternScan);
                
                var maxScanMB = this.Settings.MaxScanSizeMB;
                if (ImGui.InputInt("Max Scan Size (MB)", ref maxScanMB, 10, 100))
                {
                    if (maxScanMB < 1) maxScanMB = 1;
                    if (maxScanMB > 1000) maxScanMB = 1000;
                    this.Settings.MaxScanSizeMB = maxScanMB;
                }

                ImGui.Separator();

                if (ImGui.Button("Scan All Known Patterns"))
                {
                    ScanAllKnownPatterns();
                }

                if (ImGui.Button("Dump Patterns & Offsets"))
                {
                    DumpPatternsAndOffsets();
                }
            }

            if (ImGui.CollapsingHeader("Custom Pattern Scanner"))
            {
                ImGui.TextWrapped("Enter pattern bytes (hex, space-separated, use ?? for wildcards):");
                ImGui.InputTextMultiline("##PatternInput", ref _customPatternInput, 1024, new System.Numerics.Vector2(-1, 100));
                
                if (ImGui.Button("Scan Custom Pattern"))
                {
                    ScanCustomPattern(_customPatternInput);
                }

                if (!string.IsNullOrEmpty(_lastScanResult))
                {
                    ImGui.Separator();
                    ImGui.TextWrapped(_lastScanResult);
                }
            }

            if (ImGui.CollapsingHeader("Scan Results"))
            {
                if (_scanResults.Count > 0)
                {
                    if (ImGui.BeginTable("ScanResults", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable))
                    {
                        ImGui.TableSetupColumn("Pattern");
                        ImGui.TableSetupColumn("Address");
                        ImGui.TableSetupColumn("Offset");
                        ImGui.TableSetupColumn("Valid");
                        ImGui.TableHeadersRow();

                        foreach (var result in _scanResults)
                        {
                            ImGui.TableNextRow();
                            ImGui.TableNextColumn();
                            ImGui.Text(result.PatternName);
                            ImGui.TableNextColumn();
                            ImGui.Text($"0x{result.Address.ToInt64():X}");
                            ImGui.TableNextColumn();
                            ImGui.Text($"0x{result.Offset:X}");
                            ImGui.TableNextColumn();
                            ImGui.TextColored(result.Valid ? new System.Numerics.Vector4(0, 1, 0, 1) : new System.Numerics.Vector4(1, 0, 0, 1),
                                result.Valid ? "Yes" : "No");
                        }

                        ImGui.EndTable();
                    }
                }
                else
                {
                    ImGui.Text("No scan results yet. Click 'Scan All Known Patterns' to start.");
                }
            }

            if (ImGui.CollapsingHeader("Offset Reference"))
            {
                ImGui.TextWrapped("Known offsets within structures:");
                
                if (ImGui.BeginTable("OffsetInfo", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable))
                {
                    ImGui.TableSetupColumn("Name");
                    ImGui.TableSetupColumn("Offset");
                    ImGui.TableSetupColumn("Description");
                    ImGui.TableHeadersRow();

                    foreach (var offset in _offsetInfo.Values.OrderBy(o => o.Name))
                    {
                        ImGui.TableNextRow();
                        ImGui.TableNextColumn();
                        ImGui.Text(offset.Name);
                        ImGui.TableNextColumn();
                        ImGui.Text($"0x{offset.Offset:X}");
                        ImGui.TableNextColumn();
                        ImGui.TextWrapped(offset.Description);
                    }

                    ImGui.EndTable();
                }
            }
        }

        
        public override void DrawUI()
        {
            
            
        }

        
        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($"[PatternTool] Failed to save settings: {ex.Message}", ex);
            }
        }

        private void LoadSettings()
        {
            try
            {
                var settingsPath = Path.Combine(this.DllDirectory, "settings.json");
                if (File.Exists(settingsPath))
                {
                    var json = File.ReadAllText(settingsPath);
                    var loaded = JsonConvert.DeserializeObject<PatternToolSettings>(json);
                    if (loaded != null)
                    {
                        this.Settings = loaded;
                        _dumpDirectory = Path.Combine(this.DllDirectory, this.Settings.OutputDirectory);
                        Directory.CreateDirectory(_dumpDirectory);
                    }
                }
            }
            catch (Exception ex)
            {
                CategorizedLogger.LogError($"[PatternTool] Failed to load settings: {ex.Message}", ex);
            }
        }

        private void InitializeOffsetInfo()
        {
            
            _offsetInfo["IN_GAME_STATE_INDEX"] = new OffsetInfo
            {
                Name = "IN_GAME_STATE_INDEX",
                Offset = 4, 
                Description = "Index in GameStates array for InGameState pointer"
            };

            
            _offsetInfo["AREA_INSTANCE_DATA"] = new OffsetInfo
            {
                Name = "AREA_INSTANCE_DATA",
                Offset = 0x948,
                Description = "AreaInstanceData pointer offset in InGameState"
            };

            _offsetInfo["INGAME_DATA_OFFSET"] = new OffsetInfo
            {
                Name = "INGAME_DATA_OFFSET",
                Offset = 0x370,
                Description = "Offset to IngameData in InGameState"
            };

            
            _offsetInfo["INGAME_DATA_ENTITY_LIST"] = new OffsetInfo
            {
                Name = "INGAME_DATA_ENTITY_LIST",
                Offset = 0x490,
                Description = "EntityList pointer offset in IngameData"
            };

            _offsetInfo["INGAME_DATA_ENTITIES_COUNT"] = new OffsetInfo
            {
                Name = "INGAME_DATA_ENTITIES_COUNT",
                Offset = 0x498,
                Description = "Entities count offset in IngameData"
            };

            _offsetInfo["INGAME_DATA_LOCAL_PLAYER"] = new OffsetInfo
            {
                Name = "INGAME_DATA_LOCAL_PLAYER",
                Offset = 0x408,
                Description = "Local player pointer offset in IngameData (0x408 in source, was 0xA10 in area? verify)"
            };

            
            _offsetInfo["LOCAL_PLAYER_PTR"] = new OffsetInfo
            {
                Name = "LOCAL_PLAYER_PTR",
                Offset = 0xA10,
                Description = "Local player pointer offset in AreaInstance"
            };

            _offsetInfo["ENTITY_LIST_PTR"] = new OffsetInfo
            {
                Name = "ENTITY_LIST_PTR",
                Offset = 0xB50,
                Description = "EntityList pointer offset in AreaInstance (Confirmed by Graph Fuzzer BFS Tree, was 0x13F8)"
            };

            _offsetInfo["TERRAIN_LIST_PTR"] = new OffsetInfo
            {
                Name = "TERRAIN_LIST_PTR",
                Offset = 0x12C8,
                Description = "Terrain/Exits list pointer offset in AreaInstance (Count ~260)"
            };

            _offsetInfo["TERRAIN_GRID_PTR"] = new OffsetInfo
            {
                Name = "TERRAIN_GRID_PTR",
                Offset = 0x08,
                Description = "Terrain grid pointer offset (was 0x30)"
            };

            _offsetInfo["TERRAIN_DIMENSIONS_PTR"] = new OffsetInfo
            {
                Name = "TERRAIN_DIMENSIONS_PTR",
                Offset = 0x28,
                Description = "Terrain dimensions pointer offset"
            };

            
            _offsetInfo["COMPONENT_LIST_PTR"] = new OffsetInfo
            {
                Name = "COMPONENT_LIST_PTR",
                Offset = 0x10,
                Description = "Component list pointer offset inside ItemBase/Entity"
            };

            
            _offsetInfo["COMPONENT_INDEX_DEBUFFS"] = new OffsetInfo
            {
                Name = "COMPONENT_INDEX_DEBUFFS",
                Offset = 3,
                Description = "Index in component list for Debuffs component"
            };

            
            _offsetInfo["LIFE_COMPONENT_HEALTH"] = new OffsetInfo
            {
                Name = "LIFE_COMPONENT_HEALTH",
                Offset = 0x1A8,
                Description = "Health VitalStruct offset in Life component"
            };

            _offsetInfo["LIFE_COMPONENT_MANA"] = new OffsetInfo
            {
                Name = "LIFE_COMPONENT_MANA",
                Offset = 0x1F8,
                Description = "Mana VitalStruct offset in Life component"
            };

            _offsetInfo["LIFE_COMPONENT_ES"] = new OffsetInfo
            {
                Name = "LIFE_COMPONENT_ES",
                Offset = 0x230,
                Description = "Energy Shield VitalStruct offset in Life component"
            };

            _offsetInfo["LIFE_COMPONENT_BUFFS"] = new OffsetInfo
            {
                Name = "LIFE_COMPONENT_BUFFS",
                Offset = 0x58,
                Description = "Buffs pointer offset in Life component"
            };

            
            _offsetInfo["DEBUFF_COMPONENT_LIST"] = new OffsetInfo
            {
                Name = "DEBUFF_COMPONENT_LIST",
                Offset = 0x160,
                Description = "Debuff list pointer offset in Debuff component"
            };

            
            _offsetInfo["VITAL_RESERVED_FLAT"] = new OffsetInfo
            {
                Name = "VITAL_RESERVED_FLAT",
                Offset = 0x10,
                Description = "Reserved flat value offset in VitalStruct"
            };

            _offsetInfo["VITAL_RESERVED_PERCENT"] = new OffsetInfo
            {
                Name = "VITAL_RESERVED_PERCENT",
                Offset = 0x14,
                Description = "Reserved percent value offset in VitalStruct"
            };

            _offsetInfo["VITAL_TOTAL"] = new OffsetInfo
            {
                Name = "VITAL_TOTAL",
                Offset = 0x2C,
                Description = "Max value offset in VitalStruct"
            };

            _offsetInfo["VITAL_CURRENT"] = new OffsetInfo
            {
                Name = "VITAL_CURRENT",
                Offset = 0x30,
                Description = "Current value offset in VitalStruct"
            };

            
            _offsetInfo["POSITION_X"] = new OffsetInfo
            {
                Name = "POSITION_X",
                Offset = 0x138,
                Description = "X position offset in Render/Position component"
            };

            _offsetInfo["POSITION_Y"] = new OffsetInfo
            {
                Name = "POSITION_Y",
                Offset = 0x13C,
                Description = "Y position offset in Render/Position component"
            };

            _offsetInfo["POSITION_Z"] = new OffsetInfo
            {
                Name = "POSITION_Z",
                Offset = 0x140,
                Description = "Z position offset in Render/Position component"
            };

            
            _offsetInfo["COMPONENT_OWNER_ENTITY"] = new OffsetInfo
            {
                Name = "COMPONENT_OWNER_ENTITY",
                Offset = 0x08,
                Description = "Owner entity pointer offset in Component Header (to check Owner)"
            };
        }

        private void ScanAllKnownPatterns()
        {
            _scanResults.Clear();
            
            if (Core.Process.Address == IntPtr.Zero)
            {
                _lastScanResult = "ERROR: Process not available";
                return;
            }

            try
            {
                var baseAddress = Core.Process.Address;
                var patterns = StaticOffsetsPatterns.Patterns;
                var staticAddresses = Core.Process.StaticAddresses;

                foreach (var pattern in patterns)
                {
                    var result = new PatternScanResult
                    {
                        PatternName = pattern.Name,
                        PatternString = ReconstructPatternString(pattern),
                        Valid = false
                    };

                    if (staticAddresses.TryGetValue(pattern.Name, out var address) && address != IntPtr.Zero)
                    {
                        result.Address = address;
                        result.Offset = address.ToInt64() - baseAddress.ToInt64();
                        result.Valid = true;
                    }

                    _scanResults.Add(result);
                }

                _lastScanResult = $"Scanned {patterns.Length} patterns. Found {_scanResults.Count(r => r.Valid)} valid addresses.";
            }
            catch (Exception ex)
            {
                _lastScanResult = $"ERROR: {ex.Message}";
                CategorizedLogger.LogError($"[PatternTool] Scan error: {ex.Message}", ex);
            }
        }

        private void ScanCustomPattern(string patternString)
        {
            if (string.IsNullOrWhiteSpace(patternString))
            {
                _lastScanResult = "ERROR: Pattern string is empty";
                return;
            }

            if (Core.Process.Address == IntPtr.Zero)
            {
                _lastScanResult = "ERROR: Process not available";
                return;
            }

            try
            {
                
                var pattern = new Pattern("Custom", patternString);
                var baseAddress = Core.Process.Address;
                var reader = Core.Process.Handle;
                var scanSize = this.Settings.MaxScanSizeMB * 1024 * 1024;

                var buffer = reader.ReadMemoryArray<byte>(baseAddress, scanSize);
                if (buffer == null || buffer.Length < pattern.Data.Length)
                {
                    _lastScanResult = "ERROR: Cannot read process memory";
                    return;
                }

                var matches = new List<IntPtr>();
                for (int i = 0; i <= buffer.Length - pattern.Data.Length; i++)
                {
                    bool match = true;
                    for (int j = 0; j < pattern.Data.Length; j++)
                    {
                        if (pattern.Mask[j] && buffer[i + j] != pattern.Data[j])
                        {
                            match = false;
                            break;
                        }
                    }

                    if (match)
                    {
                        matches.Add(new IntPtr(baseAddress.ToInt64() + i));
                    }
                }

                _lastScanResult = $"Found {matches.Count} matches for pattern '{patternString}'";
                if (matches.Count > 0 && matches.Count <= 10)
                {
                    _lastScanResult += "\nMatches:\n";
                    foreach (var match in matches)
                    {
                        var offset = match.ToInt64() - baseAddress.ToInt64();
                        _lastScanResult += $"  0x{match.ToInt64():X} (offset: 0x{offset:X})\n";
                    }
                }
            }
            catch (Exception ex)
            {
                _lastScanResult = $"ERROR: {ex.Message}";
                CategorizedLogger.LogError($"[PatternTool] Custom scan error: {ex.Message}", ex);
            }
        }

        private void DumpPatternsAndOffsets()
        {
            try
            {
                var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                var filename = Path.Combine(_dumpDirectory, $"pattern_tool_dump_{timestamp}.txt");
                var sb = new StringBuilder();

                var baseAddress = Core.Process.Address;
                var staticAddresses = Core.Process.StaticAddresses;

                sb.AppendLine("=".PadRight(80, '='));
                sb.AppendLine("PATTERN TOOL DUMP - Patterns & Offsets");
                sb.AppendLine($"Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
                sb.AppendLine($"Base Address: 0x{baseAddress.ToInt64():X}");
                sb.AppendLine("=".PadRight(80, '='));
                sb.AppendLine();

                
                sb.AppendLine("KNOWN PATTERNS:");
                sb.AppendLine("-".PadRight(80, '-'));
                foreach (var pattern in StaticOffsetsPatterns.Patterns)
                {
                    var patternString = ReconstructPatternString(pattern);
                    var found = staticAddresses.TryGetValue(pattern.Name, out var address);
                    
                    sb.AppendLine($"Pattern: {pattern.Name}");
                    sb.AppendLine($"  Pattern String: {patternString}");
                    sb.AppendLine($"  Bytes To Skip: {pattern.BytesToSkip}");
                    if (found && address != IntPtr.Zero)
                    {
                        var offset = address.ToInt64() - baseAddress.ToInt64();
                        sb.AppendLine($"  Address: 0x{address.ToInt64():X}");
                        sb.AppendLine($"  Offset: 0x{offset:X} ({offset})");
                        sb.AppendLine($"  IDA: Press G -> {offset:X}");
                    }
                    else
                    {
                        sb.AppendLine($"  Address: NOT FOUND");
                    }
                    sb.AppendLine();
                }

                
                sb.AppendLine();
                sb.AppendLine("=".PadRight(80, '='));
                sb.AppendLine("KNOWN OFFSETS:");
                sb.AppendLine("-".PadRight(80, '-'));
                foreach (var offset in _offsetInfo.Values.OrderBy(o => o.Name))
                {
                    sb.AppendLine($"{offset.Name}:");
                    sb.AppendLine($"  Offset: 0x{offset.Offset:X} ({offset.Offset})");
                    sb.AppendLine($"  Description: {offset.Description}");
                    sb.AppendLine();
                }

                File.WriteAllText(filename, sb.ToString());
                _lastScanResult = $"SUCCESS: Dumped to {filename}";
            }
            catch (Exception ex)
            {
                _lastScanResult = $"ERROR: {ex.Message}";
                CategorizedLogger.LogError($"[PatternTool] Dump error: {ex.Message}", ex);
            }
        }

        private static string ReconstructPatternString(Pattern pattern)
        {
            try
            {
                var parts = new List<string>();
                for (int i = 0; i < pattern.Data.Length && i < pattern.Mask.Length; i++)
                {
                    if (pattern.BytesToSkip >= 0 && i == pattern.BytesToSkip)
                    {
                        parts.Add("^");
                    }

                    if (pattern.Mask[i])
                    {
                        parts.Add($"{pattern.Data[i]:X2}");
                    }
                    else
                    {
                        parts.Add("??");
                    }
                }
                return string.Join(" ", parts);
            }
            catch
            {
                return "N/A";
            }
        }
    }
}
