using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Decal.Interop.Core;
using Decal.Interop.Input;

namespace Decal.DecalInput
{
    [ComVisible(true)]
    [Guid("B33307BA-706D-474A-80B9-70BB8D13EF3E")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DecalInput.InputService")]
    public class InputServiceImpl : IInputService, IDecalService
    {
        internal static InputServiceImpl Instance { get; private set; }

        private IDecalCore _decalCore;
        private readonly List<HotkeyImpl> _hotkeys = new List<HotkeyImpl>();
        private readonly List<TimerImpl> _timers = new List<TimerImpl>();
        private readonly List<WinMsgHookImpl> _msgHooks = new List<WinMsgHookImpl>();
        private readonly Dictionary<string, int> _commands = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        private WndMsgImpl _message;

        internal InputBufferImpl ActiveBuffer { get; set; }

        public DecalCore Decal => (DecalCore)_decalCore;

        // Parameterized property - KeyByName takes a string parameter via IDispatch
        public int KeyByName => 0;

        internal int GetKeyByName(string name)
        {
            return KeyNames.VKFromName(name);
        }

        // IDecalService
        public void Initialize(DecalCore pDecal)
        {
            _decalCore = (IDecalCore)pDecal;
            Instance = this;
            _message = new WndMsgImpl();
        }

        public void BeforePlugins()
        {
            // In native code: subclasses game window and loads keymap
            // Window hooking requires being in-process, handled by Inject.DLL
        }

        public void AfterPlugins()
        {
            _commands.Clear();
            // Native code restores original window procedure here
        }

        public void Terminate()
        {
            _hotkeys.Clear();
            _timers.Clear();
            _msgHooks.Clear();
            _commands.Clear();
            _message = null;
            ActiveBuffer = null;

            if (Instance == this)
                Instance = null;
        }

        // Registration methods for child objects
        internal void RegisterHotkey(HotkeyImpl hotkey)
        {
            if (!_hotkeys.Contains(hotkey))
                _hotkeys.Add(hotkey);
        }

        internal void UnregisterHotkey(HotkeyImpl hotkey)
        {
            _hotkeys.Remove(hotkey);
        }

        internal void RegisterTimer(TimerImpl timer)
        {
            if (!_timers.Contains(timer))
                _timers.Add(timer);
        }

        internal void UnregisterTimer(TimerImpl timer)
        {
            _timers.Remove(timer);
        }

        internal void RegisterMsgHook(WinMsgHookImpl hook)
        {
            if (!_msgHooks.Contains(hook))
                _msgHooks.Add(hook);
        }

        internal void UnregisterMsgHook(WinMsgHookImpl hook)
        {
            _msgHooks.Remove(hook);
        }

        /// <summary>
        /// Called from the window hook procedure for each window message.
        /// Returns true if the message should be eaten (suppressed).
        /// </summary>
        internal bool ProcessWindowMessage(int hwnd, int msg, int wParam, int lParam)
        {
            _message.Set(hwnd, msg, wParam, lParam);

            // Process message hooks
            foreach (var hook in _msgHooks)
            {
                hook.FireMessage(hook, _message);
                if (_message.Eat)
                    return true;
            }

            // Process hotkeys on WM_KEYUP (0x0101), only if no active buffer
            if (msg == 0x0101 && ActiveBuffer == null)
            {
                foreach (var hotkey in _hotkeys)
                {
                    if (hotkey.Enabled && hotkey.Key.Length > 0)
                    {
                        // Match virtual key
                        int vk = wParam;
                        string keyName = hotkey.Key;
                        int hotkeyVk;

                        if (keyName.StartsWith("{") && keyName.EndsWith("}"))
                        {
                            string name = keyName.Substring(1, keyName.Length - 2);
                            hotkeyVk = KeyNames.VKFromName(name);
                        }
                        else
                        {
                            hotkeyVk = char.ToUpper(keyName[0]);
                        }

                        if (vk == hotkeyVk)
                        {
                            hotkey.FireHotkey();
                            break; // Only one hotkey per key press
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Called from the render loop to process timers and input buffer delays.
        /// </summary>
        internal void ProcessTimers()
        {
            uint now = GetTickCount();
            // Copy list to avoid modification during iteration
            var timersCopy = new List<TimerImpl>(_timers);
            foreach (var timer in timersCopy)
            {
                timer.CheckTimeout(now);
            }
        }

        [DllImport("kernel32.dll")]
        private static extern uint GetTickCount();
    }
}
