using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using FairyGUI;
using NBC;
// using Rewired;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;

namespace NBF
{
    public enum ControllerType
    {
        KeyboardMouse = 0,
        GamePad = 1
    }

    public enum UIInputButtonShowMode
    {
        None,
        MenuLeft,
        MenuRight,
        SubMenuLeft,
        SubMenuRight,
        BottomLeft,
        BottomRight,
    }

    public class UIInputInvoke
    {
        public object UIObject;
        public InputInvokeAttribute InputInvoke;
        public MethodInfo MethodInfo;
    }

    public class InputManager : MonoService<InputManager>
    {
        public static bool IsOp1;
        public static bool IsOp2;


        public const string InputMapUI = "UI";
        public const string InputMapPlayer = "Player";

        public static event Action<bool> OnOp1Action;
        public static event Action<bool> OnOp2Action;

        /// <summary>
        /// 执行输入事件
        /// </summary>
        public static event Action<string> OnUIPerformed;

        /// <summary>
        /// 执行输入事件完毕
        /// </summary>
        public static event Action<string> OnUICanceled;

        /// <summary>
        /// 执行输入事件
        /// </summary>
        public static event Action<string> OnPlayerPerformed;

        /// <summary>
        /// 执行输入事件完毕
        /// </summary>
        public static event Action<string> OnPlayerCanceled;

        /// <summary>
        /// 执行输入事件
        /// </summary>
        public static event Action<InputAction.CallbackContext> OnPlayerValuePerformed;

        /// <summary>
        /// 执行输入事件完毕
        /// </summary>
        public static event Action<InputAction.CallbackContext> OnPlayerValueCanceled;

        /// <summary>
        /// 触发交互游戏对象
        /// </summary>
        public static event Action<InteractiveObject> OnInteractiveObjectAction;

        public static event Action OnUIInvokeChange;


        public static PlayerInputControl PlayerInputControl { get; private set; }

        /// <summary>
        /// 手柄输入
        /// </summary>
        public static bool IsControllerInput;

        /// <summary>
        /// ui阻止游戏输入
        /// </summary>
        public static bool IsUIStopInput;

        public static ControllerType ControllerType = ControllerType.KeyboardMouse;


        private InputActionMap _uiInputActionMap;

        protected override void OnAwake()
        {
            InputCursorExtension.InputInit();
            DontDestroyOnLoad(gameObject);
            PlayerInputControl = new PlayerInputControl();
            PlayerInputControl.Enable();
        }

        private void Start()
        {
            AddEvent();
        }

        private void OnDestroy()
        {
            RemoveEvent();
            InputCursorExtension.Dispose();
        }


        public static void SetMouseCursor(bool val)
        {
            if (val)
            {
                if (ControllerType == ControllerType.KeyboardMouse)
                {
                    Cursor.visible = true;
                }

                Cursor.lockState = CursorLockMode.None;
            }
            else if (ControllerType == ControllerType.KeyboardMouse)
            {
                Cursor.visible = false;
            }

            Cursor.visible = val;
            if (!val)
            {
                Cursor.lockState = CursorLockMode.Confined;
            }
        }

        public static Vector2 GetMovementInput()
        {
            if (IsUIStopInput) return Vector2.zero;
            return PlayerInputControl.Player.Move?.ReadValue<Vector2>() ?? Vector2.zero;
        }

        public static Vector2 GetLookInput()
        {
            if (IsUIStopInput) return Vector2.zero;
            return PlayerInputControl.Player.Look?.ReadValue<Vector2>() ?? Vector2.zero;
        }


        private void AddEvent()
        {
            CacheInputActionIcons(PlayerInputControl.asset.actionMaps);
            foreach (var actionMap in PlayerInputControl.asset.actionMaps)
            {
                actionMap.Enable();
                if (actionMap.name == InputMapUI)
                {
                    _uiInputActionMap = actionMap;
                    foreach (var action in actionMap.actions)
                    {
                        if (action.type == InputActionType.Button)
                        {
                            action.performed += OnUIButtonPerformed;
                            action.canceled += OnUIButtonCanceled;
                        }

                        //var fileName = binding.effectivePath.Replace("<", "").Replace(">", "").Replace("/", "_");
                    }
                }
                else if (actionMap.name == InputMapPlayer)
                {
                    foreach (var action in actionMap.actions)
                    {
                        if (action.type == InputActionType.Button)
                        {
                            action.performed += OnPlayerButtonPerformed;
                            action.canceled += OnPlayerButtonCanceled;
                        }
                        else if (action.type == InputActionType.Value)
                        {
                            action.performed += OnInputPlayerValuePerformed;
                            action.canceled += OnInputPlayerValueCanceled;
                        }
                    }
                }
            }
        }

        private void RemoveEvent()
        {
            OnUIPerformed = null;
            OnUICanceled = null;
            OnPlayerPerformed = null;
            OnPlayerCanceled = null;
        }

        private void OnUIButtonPerformed(InputAction.CallbackContext context)
        {
            OnUIPerformed?.Invoke(context.action.name);
        }

        private void OnUIButtonCanceled(InputAction.CallbackContext context)
        {
            OnUICanceled?.Invoke(context.action.name);
            InvokeUIInput(context.action.name);
        }

        private void OnPlayerButtonPerformed(InputAction.CallbackContext context)
        {
            if (IsUIStopInput) return;
            var actionName = context.action.name;
            if (actionName == "Op1")
            {
                IsOp1 = true;
                OnOp1Action?.Invoke(true);
            }
            else if (actionName == "Op2")
            {
                OnOp2Action?.Invoke(true);
            }

            OnPlayerPerformed?.Invoke(actionName);
        }

        private void OnPlayerButtonCanceled(InputAction.CallbackContext context)
        {
            if (IsUIStopInput) return;
            var actionName = context.action.name;
            if (actionName == "Op1")
            {
                IsOp1 = false;
                OnOp1Action?.Invoke(false);
            }
            else if (actionName == "Op2")
            {
                OnOp2Action?.Invoke(false);
            }

            OnPlayerCanceled?.Invoke(actionName);
        }

        private void OnInputPlayerValuePerformed(InputAction.CallbackContext context)
        {
            if (IsUIStopInput) return;
            OnPlayerValuePerformed?.Invoke(context);
        }

        private void OnInputPlayerValueCanceled(InputAction.CallbackContext context)
        {
            if (IsUIStopInput) return;
            OnPlayerValueCanceled?.Invoke(context);
        }


        public void OnInteractiveObject(InteractiveObject interactiveObject)
        {
            Debug.LogError($"OnInteractiveObject {interactiveObject != null}");
            OnInteractiveObjectAction?.Invoke(interactiveObject);
        }

        public void SendUIInput(string actionName)
        {
            OnUIPerformed?.Invoke(actionName);
            OnUICanceled?.Invoke(actionName);
            InvokeUIInput(actionName);
        }

        #region UI界面按键管理

        private readonly Dictionary<object, List<UIInputInvoke>> _panelActions =
            new Dictionary<object, List<UIInputInvoke>>();

        // private Dictionary<string, string> _keyboardIcons = new Dictionary<string, string>();

        private Dictionary<string, InputIconData> InputIconData = new Dictionary<string, InputIconData>();

        public void On(object obj)
        {
            var ms = Reflection.GetMethodsAttribute<InputInvokeAttribute>(obj.GetType());
            List<UIInputInvoke> inputInvokes = new List<UIInputInvoke>();
            foreach (var kv in ms)
            {
                var invokeData = new UIInputInvoke()
                {
                    MethodInfo = kv.Key,
                    UIObject = obj,
                    InputInvoke = kv.Value
                };
                inputInvokes.Add(invokeData);
            }

            if (inputInvokes.Count > 0)
            {
                _panelActions[obj] = inputInvokes;
            }

            OnUIInvokeChange?.Invoke();
        }

        public void Off(object obj)
        {
            _panelActions.Remove(obj, out List<UIInputInvoke> inputInvokes);
            OnUIInvokeChange?.Invoke();
        }


        public void InvokeUIInput(string actionName)
        {
            foreach (var obj in _panelActions.Keys)
            {
                var invokes = _panelActions[obj];
                foreach (var uiInputInvoke in invokes)
                {
                    if (uiInputInvoke.InputInvoke.Name != actionName) continue;
                    if (uiInputInvoke.UIObject is IUIPanel panel)
                    {
                        if (!panel.IsShowing) continue;
                        if (!panel.IsTop) continue;
                    }
                    else if (uiInputInvoke.UIObject is GObject gObject)
                    {
                        if (!gObject.visible) continue;
                    }

                    uiInputInvoke.MethodInfo.Invoke(uiInputInvoke.UIObject, null);
                }
            }
        }

        public List<UIInputInvoke> GetUsableInvokes()
        {
            List<UIInputInvoke> ret = new List<UIInputInvoke>();

            foreach (var keyValuePair in _panelActions.Values)
            {
                foreach (var uiInputInvoke in keyValuePair)
                {
                    if (uiInputInvoke.UIObject is IUIPanel { IsTop: true })
                    {
                        ret.Add(uiInputInvoke);
                    }
                }
            }

            return ret;
        }
        

        public string GetInputIcon(string map, string actionName)
        {
            if (InputIconData.TryGetValue(map, out var data))
            {
                return data.GetIcon(actionName);
            }

            return string.Empty;
        }


        private void CacheInputActionIcons(ReadOnlyArray<InputActionMap> actionMaps)
        {
            foreach (var actionMap in actionMaps)
            {
                var data = new InputIconData();
                data.CacheInputActionIcons(actionMap.actions);
                InputIconData[actionMap.name] = data;
            }
        }

        #endregion
    }
}