using System.Collections.Generic;
using UnityEngine;

namespace AxibugEmuOnline.Client.InputDevices
{
    public class InputDevicesManager
    {
        InputResolver m_inputResolver = InputResolver.Create();
        Dictionary<string, InputDevice> m_devices = new Dictionary<string, InputDevice>();

        public delegate void OnDeviceConnectedHandle(InputDevice connectDevice);
        public event OnDeviceConnectedHandle OnDeviceConnected;
        public delegate void OnDeviceLostHandle(InputDevice lostDevice);
        public event OnDeviceLostHandle OnDeviceLost;

        public InputDevicesManager()
        {
            m_inputResolver.OnDeviceConnected += Resolver_OnDeviceConnected;
            m_inputResolver.OnDeviceLost += Resolver_OnDeviceLost;
            foreach (var device in m_inputResolver.GetDevices())
                AddDevice(device);
        }

        private void Resolver_OnDeviceLost(InputDevice lostDevice)
        {
            RemoveDevice(lostDevice);
        }

        private void Resolver_OnDeviceConnected(InputDevice connectDevice)
        {
            AddDevice(connectDevice);
        }

        void AddDevice(InputDevice device)
        {
            m_devices[device.UniqueName] = device;
            OnDeviceConnected?.Invoke(device);
        }

        void RemoveDevice(InputDevice device)
        {
            m_devices.Remove(device.UniqueName);
            OnDeviceLost?.Invoke(device);
        }

        /// <summary>
        /// 获得一个键盘设备
        /// </summary>
        public KeyBoard GetKeyboard()
        {
            foreach (var d in m_devices.Values)
            {
                if (d is KeyBoard kb) return kb;
            }

            return null;
        }

        /// <summary> 由外部驱动的逻辑更新入口 </summary>
        public void Update()
        {
            foreach (var device in m_devices.Values) device.Update();
        }
    }

    public abstract class InputDevice
    {
        public string UniqueName => m_resolver.GetDeviceName(this);

        /// <summary> 指示该设备是否在线 </summary>
        public bool Online => m_resolver.CheckOnline(this);
        /// <summary> 指示该设备当前帧是否有任意控件被激发 </summary>
        public bool AnyKeyDown { get; private set; }
        /// <summary> 获得输入解决器 </summary>
        internal InputResolver Resolver => m_resolver;

        protected Dictionary<string, InputControl> m_controlMapper = new Dictionary<string, InputControl>();
        protected InputResolver m_resolver;
        public InputDevice(InputResolver resolver)
        {
            m_resolver = resolver;

            foreach (var control in DefineControls())
            {
                m_controlMapper.Add(control.ControlName, control);
            }
        }

        public void Update()
        {
            AnyKeyDown = false;

            foreach (var control in m_controlMapper.Values)
            {
                if (control.Start)
                {
                    AnyKeyDown = true;
                }
            }
        }

        /// <summary> 用于列出这个输入设备的所有输入控件实例 </summary>
        /// <returns></returns>
        protected abstract IEnumerable<InputControl> DefineControls();

        /// <summary> 通过控件名称,找到对应的控件 </summary>
        /// <param name="keyName"></param>
        /// <returns></returns>
        public InputControl FindControlByName(string controlName)
        {
            m_controlMapper.TryGetValue(controlName, out var key);
            return key;
        }

        /// <summary>
        /// 输入设备的抽象控件接口
        /// </summary>
        public abstract class InputControl
        {
            /// <summary> 控件所属设备 </summary>
            public InputDevice Device { get; internal set; }

            /// <summary> 获取该控件是否在当前调用帧被激发 </summary>
            public abstract bool Start { get; }
            /// <summary> 获取该控件是否在当前调用帧被释放 </summary>
            public abstract bool Release { get; }
            /// <summary> 获取该控件是否在当前调用帧是否处于活动状态 </summary>
            public abstract bool Performing { get; }

            /// <summary> 获得该控件的以二维向量表达的值 </summary>
            /// <returns></returns>
            public abstract Vector2 GetVector2();
            /// <summary> 获得该控件的以浮点数表达的值 </summary>
            public abstract float GetFlaot();

            /// <summary> 控件名,这个控件名称必须是唯一的 </summary>
            public abstract string ControlName { get; }

            internal InputControl(InputDevice device)
            {
                Device = device;
            }
        }

    }
}