using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System;

namespace TFramework
{
    [DefaultExecutionOrder(-999)]
    public class InputManager : BaseManager
    {
        /// <summary>
        /// 是否启用输入设备
        /// </summary>
        [SerializeField]
        private bool m_isEnableInputDevice = true;

        [SerializeField]
        private string m_inputDevice;

        public int m_checkError=1;

        private Dictionary<string, V_Axis> _v_axes = new Dictionary<string, V_Axis>();
        private Dictionary<string, V_Key> _v_keys = new Dictionary<string, V_Key>();

        private InputDeviceBase _inputDevice;
        [SerializeField]
        private float _doubleClickInterval = 0.2f;
        /// <summary>
        /// 双击时间间隔
        /// </summary>
        public float DoubleClickInterval { get=>_doubleClickInterval; set=>_doubleClickInterval=value; }
        private IEnumerator _clickEnumerator;
        private float _currentKeyClickTime;
        private bool _startTime = false;

        /// <summary>
        /// 输入设备
        /// </summary>
        public InputDeviceBase InputDevice => _inputDevice = _inputDevice ?? GlobalTool.CretaInstanceToCurrentDomain<InputDeviceBase>(m_inputDevice);

        /// <summary>
        /// 鼠标位置
        /// </summary>
        public Vector3 MousePosition { get; private set; }

        /// <summary>
        /// 是否启用输入设备
        /// </summary>
        public bool IsEnableInputDevice
        {
            get
            {
                return m_isEnableInputDevice;
            }
            set
            {
                m_isEnableInputDevice = value;
                if (!m_isEnableInputDevice)
                {
                    //InputDevice.OnShutdown();
                    ResetAll();
                }
                else
                    InputDevice.OnStartUp();
            }
        }

        private void Start()
        {
            if(IsEnableInputDevice)
                InputDevice?.OnStartUp();
        }

        public void Update()
        {
            if (IsEnableInputDevice)
                InputDevice?.OnRun();
        }

        /// <summary>
        /// 是否按住任意键
        /// </summary>
        public bool IsAnyKey => IsEnableInputDevice && Input.anyKey;

        /// <summary>
        /// 是否按下任意键
        /// </summary>
        public bool IsAnyKeyDown => IsEnableInputDevice && Input.anyKeyDown;

        /// <summary>
        /// 是否存在虚拟轴
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool IsExistV_Axis(string name) => _v_axes.ContainsKey(name);

        /// <summary>
        /// 是否存在虚拟键
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool IsExistV_Key(string name) => _v_keys.ContainsKey(name);

        /// <summary>
        /// 添加虚拟轴
        /// </summary>
        /// <param name="name"></param>
        public void AddV_Axis(string name)
        {
            if (!IsExistV_Axis(name))
                _v_axes.Add(name, new V_Axis(name));
        }

        /// <summary>
        /// 添加虚拟键
        /// </summary>
        /// <param name="name"></param>
        public void AddV_Key(string name)
        {
            if (!IsExistV_Key(name))
                _v_keys.Add(name, new V_Key(name));
        }

        /// <summary>
        /// 移除虚拟轴
        /// </summary>
        /// <param name="name"></param>
        public void RemoveV_Axis(string name)
        {
            if (!IsExistV_Axis(name))
                _v_axes.Remove(name);
            else
                Log.Error($"不存在虚拟轴【{name}】!");
        }

        /// <summary>
        /// 移除虚拟键
        /// </summary>
        /// <param name="name"></param>
        public void RemoveV_Key(string name)
        {
            if (IsExistV_Key(name))
                _v_keys.Remove(name);
            else
                Log.Error($"不存在虚拟键【{name}】！");
        }

        private IEnumerator CklickTime(V_Key key)
        {
            while (_currentKeyClickTime > 0)
            {
                _currentKeyClickTime -= Time.deltaTime;
                yield return YieldWaitTool.YieldWaitForFixedUpdate();
            }
            _startTime = false;
            key.Click();
        }

        #region 按键

        /// <summary>
        /// 设置按键按下
        /// </summary>
        /// <param name="name"></param>
        public void SetV_KeyDown(string name)
        {
            if (!IsExistV_Key(name))
                AddV_Key(name);
            _v_keys[name].Pressed(m_checkError);
        }

        /// <summary>
        /// 设置按键抬起
        /// </summary>
        /// <param name="name"></param>
        public void SetV_KeyUp(string name)
        {
            if (!IsExistV_Key(name))
                AddV_Key(name);
            _v_keys[name].Released(m_checkError);
            if (!_startTime)
            {
                _startTime = true;
                if (_clickEnumerator != null)
                {
                    StopCoroutine(_clickEnumerator);
                    _clickEnumerator = null;
                }
                _currentKeyClickTime = DoubleClickInterval;
                _clickEnumerator = CklickTime(_v_keys[name]);
                StartCoroutine(_clickEnumerator);
            }
        }

        /// <summary>
        /// 获取按键按下
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetV_KeyDown(string name) => GetV_KeysDown(name);

        /// <summary>
        /// 获取按键抬起
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetV_KeyUp(string name) => GetV_KeysUp(name);

        /// <summary>
        /// 按键按住
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetV_Key(string name) => GetV_Keys(name);
        
        /// <summary>
        /// 按键单击
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetV_KeyClick(string name)
        {
            if (!IsExistV_Key(name))
                AddV_Key(name);
            return _v_keys[name].GetClick;
        }

        /// <summary>
        /// 按键双击
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetV_KeyDobuleClick(string name)
        {
            if (!IsExistV_Key(name))
                AddV_Key(name);
            return _v_keys[name].GetDoubleClick;
        }
        
        /// <summary>
        /// 组合键按住
        /// </summary>
        /// <param name="names"></param>
        /// <returns></returns>
        public bool GetV_Keys(params string[] names) => GetV_KeyOp(0, names);

        /// <summary>
        /// 组合键按下
        /// </summary>
        /// <param name="names"></param>
        /// <returns></returns>
        public bool GetV_KeysDown(params string[] names) => GetV_KeyOp(-1, names);

        /// <summary>
        /// 组合键抬起
        /// </summary>
        /// <param name="names"></param>
        /// <returns></returns>
        public bool GetV_KeysUp(params string[] names) => GetV_KeyOp(1, names);

        /// <summary>
        /// 获取按键操作
        /// </summary>
        /// <param name="names"></param>
        /// <param name="op">1 抬起 0 按住 -1 按下</param>
        /// <returns></returns>
        private bool GetV_KeyOp(int op = 0, params string[] names)
        {
            for (int i = 0; i < names.Length; i++)
            {
                if (!IsExistV_Key(names[i]))
                    AddV_Key(names[i]);
            }
            //if (op == 1)
            //    return _v_keys.Where(p => names.Contains(p.Key)).Select(p => p.Value).All(p => p.GetKeyUp);
            //else if (op == 0)
            //    return _v_keys.Where(p => names.Contains(p.Key)).Select(p => p.Value).All(p => p.GetKey);
            //else
            //    return _v_keys.Where(p => names.Contains(p.Key)).Select(p => p.Value).All(p => p.GetKeyDown);
            
            if (op == 1)
            {
                for (int i = 0; i < names.Length; i++)
                {
                    if (!_v_keys[names[i]].GetKeyUp)
                        return false;
                }
                return true;
            }
            else if (op == 0)
            {
                for (int i = 0; i < names.Length; i++)
                {
                    if (!_v_keys[names[i]].GetKey)
                        return false;
                }
                return true;
            }
            else
            {
                for (int i = 0; i < names.Length; i++)
                {
                    if (!_v_keys[names[i]].GetKeyDown)
                        return false;
                }
                return true;
            }
        }

        /// <summary>
        /// 按键按住 可传组合键
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetKeys(params KeyCode[] keyCodes) => keyCodes.All(p => Input.GetKey(p));

        /// <summary>
        /// 按键抬起 可传组合键
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetKeysUp(params KeyCode[] keyCodes) => keyCodes.All(p => Input.GetKeyUp(p));

        /// <summary>
        /// 按键按下 可传组合键
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetKeyDown(params KeyCode[] keyCodes) => keyCodes.All(p => Input.GetKeyDown(p));
        #endregion

        #region 轴
        /// <summary>
        /// 设置轴线值
        /// </summary>
        /// <param name="name">轴线名称</param>
        /// <param name="value">轴线值</param>
        public void SetV_Axis(string name, float value)
        {
            if (!IsExistV_Axis(name))
                AddV_Axis(name);
            _v_axes[name].Update(value);
        }

        /// <summary>
        /// 设置轴线值为0
        /// </summary>
        /// <param name="name">轴线名称</param>
        public void SetV_AxisZero(string name) => SetV_Axis(name, 0);

        /// <summary>
        /// 设置轴线值为正方向1
        /// </summary>
        /// <param name="name">轴线名称</param>
        public void SetV_AxisPositive(string name) => SetV_Axis(name, 1);
        /// <summary>
        /// 设置轴线值为负方向-1
        /// </summary>
        /// <param name="name">轴线名称</param>
        public void SetV_AxisNegative(string name) => SetV_Axis(name, -1);

        /// <summary>
        /// 获取轴线值
        /// </summary>
        /// <param name="name">轴线名称</param>
        /// <param name="raw">是否获取整数值</param>
        /// <returns>轴线值</returns>
        public float GetAxis(string name, bool raw = false)
        {
            if (!IsExistV_Axis(name))
                AddV_Axis(name);
            return raw ? _v_axes[name].GetValueRaw : _v_axes[name].GetValue;
        }
        #endregion

        /// <summary>
        /// 设置鼠标位置
        /// </summary>
        /// <param name="value"></param>
        public void SetVirtualMousePosition(Vector3 value) => MousePosition = value;

        /// <summary>
        /// 清除所有输入状态
        /// </summary>
        public void ResetAll()
        {
            foreach (var item in _v_axes.Values)
                item.Update(0);
            foreach (var item in _v_keys.Values)
                item.Released();
        }

        /// <summary>
        /// 虚拟键
        /// </summary>
        private sealed class V_Key
        {
            public string Name { get; private set; }
            public float ClickCount 
            { 
                get=>_clickCount; 
                private set
                {
                    _clickCount = value;
                }
            }

            private float _clickCount = 0;
            public int _pressedFrame = -5;
            private int _releasedFrame = -5;
            private bool _pressed = false;
            private int _clickFrame = -5;
            private int _doubleClickFrame = -5;

            public V_Key(string name) => Name = name;

            public void Pressed(int error=0)
            {
                if (_pressed)
                    return;

                _pressed = true;
                _pressedFrame = Time.frameCount+ error;
                ClickCount += 0.5f;
            }

            public void Released(int error = 0)
            {
                if (!_pressed)
                    return;

                _pressed = false;
                _releasedFrame = Time.frameCount+ error;
                ClickCount += 0.5f;
            }

            public void Click() 
            {
                if (_clickCount >= 2)
                    _doubleClickFrame = Time.frameCount;
                else if (_clickCount > 0 && _clickCount <= 2)
                    _clickFrame = Time.frameCount;
                ClickCount = 0;
            }

            /// <summary>
            /// 按住
            /// </summary>
            public bool GetKey => _pressed;

            /// <summary>
            /// 按键按下
            /// </summary>
            public bool GetKeyDown => _pressedFrame == Time.frameCount;

            /// <summary>
            /// 按键抬起
            /// </summary>
            public bool GetKeyUp => _releasedFrame == Time.frameCount;

            /// <summary>
            /// 按键单击
            /// </summary>
            public bool GetClick => _clickFrame == Time.frameCount;

            /// <summary>
            /// 按键双击
            /// </summary>
            public bool GetDoubleClick => _doubleClickFrame == Time.frameCount;
        }

        /// <summary>
        /// 虚拟轴
        /// </summary>
        private sealed class V_Axis
        {
            public string Name { get; private set; }

            private float _value;

            public V_Axis(string name) => Name = name;

            public void Update(float value) => _value = value;

            public float GetValue => _value;

            public float GetValueRaw => _value > 0 ? 1 : (_value < 0 ? -1 : 0);
        }
    }
}
