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

namespace Decal.DHS
{
    internal class RegisteredHotkey
    {
        public string PluginCookie;
        public HotkeyImpl Hotkey;
    }

    [ComVisible(true)]
    [Guid("6B6B9FA8-37DE-4FA3-8C60-52BD6A2F9855")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(_IHotkeySystemEvents))]
    [ProgId("DHS.HotkeySystem")]
    public class HotkeySystemImpl : IHotkeySystem, IDecalService
    {
        private readonly List<RegisteredHotkey> _hotkeys = new List<RegisteredHotkey>();
        private readonly Dictionary<object, IDHSHotkeyEvents> _connectedPlugins = new Dictionary<object, IDHSHotkeyEvents>();

        // COM event — raised when a hotkey fires
        public event _IHotkeySystemEvents_HotkeyEventEventHandler HotkeyEvent;

        public void AddHotkey(string szPlugin, Hotkey pHotkey)
        {
            var impl = pHotkey as HotkeyImpl;
            if (impl == null)
            {
                // Wrap foreign IHotkey implementation
                impl = new HotkeyImpl
                {
                    EventTitle = ((IHotkey)pHotkey).EventTitle,
                    EventDescription = ((IHotkey)pHotkey).EventDescription,
                    AltState = ((IHotkey)pHotkey).AltState,
                    ControlState = ((IHotkey)pHotkey).ControlState,
                    ShiftState = ((IHotkey)pHotkey).ShiftState,
                    VirtualKey = ((IHotkey)pHotkey).VirtualKey
                };
            }

            _hotkeys.Add(new RegisteredHotkey
            {
                PluginCookie = szPlugin,
                Hotkey = impl
            });
        }

        public bool QueryHotkey(string bstrTitle)
        {
            foreach (var rh in _hotkeys)
            {
                if (string.Equals(rh.Hotkey.EventTitle, bstrTitle, StringComparison.OrdinalIgnoreCase))
                    return true;
            }
            return false;
        }

        public Hotkey QueryHotkeyEx(string szPluginCookie, string szTitle)
        {
            foreach (var rh in _hotkeys)
            {
                if (string.Equals(rh.PluginCookie, szPluginCookie, StringComparison.OrdinalIgnoreCase) &&
                    string.Equals(rh.Hotkey.EventTitle, szTitle, StringComparison.OrdinalIgnoreCase))
                {
                    return (Hotkey)(object)rh.Hotkey;
                }
            }
            return null;
        }

        public void Connect(object pPlugin)
        {
            // Plugin implements IDHSHotkeyEvents for receiving hotkey notifications
            var events = pPlugin as IDHSHotkeyEvents;
            if (events != null)
            {
                _connectedPlugins[pPlugin] = events;
            }
        }

        public void DeleteHotkey(string szPluginCookie, string szHotkeyName)
        {
            _hotkeys.RemoveAll(rh =>
                string.Equals(rh.PluginCookie, szPluginCookie, StringComparison.OrdinalIgnoreCase) &&
                string.Equals(rh.Hotkey.EventTitle, szHotkeyName, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// Called by the input system when a key combination matches a registered hotkey.
        /// Fires the HotkeyEvent and notifies connected plugins.
        /// </summary>
        internal bool FireHotkey(string eventTitle)
        {
            bool eat = false;

            // Fire COM connection point event
            HotkeyEvent?.Invoke(eventTitle, ref eat);
            if (eat) return true;

            // Notify connected plugins via IDHSHotkeyEvents
            foreach (var plugin in _connectedPlugins.Values)
            {
                try
                {
                    if (plugin.OnHotkey(eventTitle))
                        return true;
                }
                catch
                {
                    // Plugin error — don't propagate
                }
            }

            return false;
        }

        /// <summary>
        /// Check if a key press matches any registered hotkey and fire it.
        /// </summary>
        internal bool ProcessKey(int virtualKey, bool alt, bool ctrl, bool shift)
        {
            foreach (var rh in _hotkeys)
            {
                var hk = rh.Hotkey;
                if (hk.VirtualKey == virtualKey &&
                    hk.AltState == alt &&
                    hk.ControlState == ctrl &&
                    hk.ShiftState == shift)
                {
                    if (FireHotkey(hk.EventTitle))
                        return true;
                }
            }
            return false;
        }

        // IDecalService implementation
        public void Initialize(DecalCore pDecal)
        {
            // DHS doesn't need DecalCore reference directly
        }

        public void BeforePlugins()
        {
        }

        public void AfterPlugins()
        {
        }

        public void Terminate()
        {
            _hotkeys.Clear();
            _connectedPlugins.Clear();
        }
    }
}
