using System;
using System.Collections.Generic;
using Unity.BossRoom.Gameplay.UserInput;
using Unity.BossRoom.Gameplay.GameplayObjects;
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
using UnityEngine;
using Action = Unity.BossRoom.Gameplay.Actions.Action;
using SkillTriggerStyle = Unity.BossRoom.Gameplay.UserInput.ClientInputSender.SkillTriggerStyle;

namespace Unity.BossRoom.Gameplay.UI
{
    /// <summary>
    /// Provides logic for a Hero Action Bar with attack, skill buttons and a button to open emotes panel
    /// This bar tracks button clicks on hero action buttons for later use by ClientInputSender
    /// </summary>
    public class HeroActionBar : MonoBehaviour
    {
        [SerializeField]
        [Tooltip("The button that activates the basic action (comparable to right-clicking the mouse)")]
        UIHUDButton m_BasicActionButton;

        [SerializeField]
        [Tooltip("The button that activates the hero's first special move")]
        UIHUDButton m_SpecialAction1Button;

        [SerializeField]
        [Tooltip("The button that activates the hero's second special move")]
        UIHUDButton m_SpecialAction2Button;
        [SerializeField]
        [Tooltip("The button that activates the hero's thid special move")]
        UIHUDButton m_SpecialAction3Button;
        [SerializeField]
        [Tooltip("The button that activates the hero's fourth special move")]
        UIHUDButton m_SpecialAction4Button;

        [SerializeField]
        [Tooltip("The button that opens/closes the Emote bar")]
        UIHUDButton m_EmoteBarButton;

        [SerializeField]
        [Tooltip("The Emote bar that will be enabled or disabled when clicking the Emote bar button")]
        GameObject m_EmotePanel;

        /// <summary>
        /// Our input-sender. Initialized in RegisterInputSender()
        /// </summary>
        ClientInputSender m_InputSender;
        public List<GameObject> CrowButtons;
        public List<GameObject> OtherButtons;

        /// <summary>
        /// Identifiers for the buttons on the action bar.
        /// </summary>
        enum ActionButtonType
        {
            BasicAction,
            Special1,
            Special2,
            EmoteBar,
            Special3,
            Special4,
        }

        /// <summary>
        /// Cached UI information about one of the buttons on the action bar.
        /// Takes care of registering/unregistering click-event messages,
        /// and routing the events into HeroActionBar.
        /// </summary>
        class ActionButtonInfo
        {
            public readonly ActionButtonType Type;
            public readonly UIHUDButton Button;
            public readonly UITooltipDetector Tooltip;

            /// <summary> T
            /// The current Action that is used when this button is pressed.
            /// </summary>
            public Action CurAction;

            readonly HeroActionBar m_Owner;

            public ActionButtonInfo(ActionButtonType type, UIHUDButton button, HeroActionBar owner)
            {
                Type = type;
                Button = button;
                Tooltip = button.GetComponent<UITooltipDetector>();
                m_Owner = owner;
            }

            public void RegisterEventHandlers()
            {
                Button.OnPointerDownEvent += OnClickDown;
                Button.OnPointerUpEvent += OnClickUp;
            }

            public void UnregisterEventHandlers()
            {
                Button.OnPointerDownEvent -= OnClickDown;
                Button.OnPointerUpEvent -= OnClickUp;
            }

            void OnClickDown()
            {
                m_Owner.OnButtonClickedDown(Type);
            }

            void OnClickUp()
            {
                m_Owner.OnButtonClickedUp(Type);
            }
        }

        /// <summary>
        /// Dictionary of info about all the buttons on the action bar.
        /// </summary>
        Dictionary<ActionButtonType, ActionButtonInfo> m_ButtonInfo;

        /// <summary>
        /// Cache the input sender from a <see cref="ClientPlayerAvatar"/> and self-initialize.
        /// </summary>
        /// <param name="clientPlayerAvatar"></param>
        void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar)
        {
            if (!clientPlayerAvatar.TryGetComponent(out ClientInputSender inputSender))
            {
                Debug.LogError("ClientInputSender not found on ClientPlayerAvatar!", clientPlayerAvatar);
            }

            if (m_InputSender != null)
            {
                Debug.LogWarning($"Multiple ClientInputSenders in scene? Discarding sender belonging to {m_InputSender.gameObject.name} and adding it for {inputSender.gameObject.name} ");
            }

            m_InputSender = inputSender;
            m_InputSender.action1ModifiedCallback += Action1ModifiedCallback;

            Action action1 = null;
            if (m_InputSender.actionState1 != null)
            {
                GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState1.actionID, out action1);
            }
            UpdateActionButton(m_ButtonInfo[ActionButtonType.BasicAction], action1);

            Action action2 = null;
            if (m_InputSender.actionState2 != null)
            {
                GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState2.actionID, out action2);
            }
            UpdateActionButton(m_ButtonInfo[ActionButtonType.Special1], action2);

            Action action3 = null;
            if (m_InputSender.actionState3 != null)
            {
                GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState3.actionID, out action3);
            }
            UpdateActionButton(m_ButtonInfo[ActionButtonType.Special2], action3);
        }

        void Action1ModifiedCallback()
        {
            var action = GameDataSource.Instance.GetActionPrototypeByID(m_InputSender.actionState1.actionID);

            UpdateActionButton(m_ButtonInfo[ActionButtonType.BasicAction],
                action,
                m_InputSender.actionState1.selectable);
        }

        void DeregisterInputSender()
        {
            if (m_InputSender)
            {
                m_InputSender.action1ModifiedCallback -= Action1ModifiedCallback;
            }
            m_InputSender = null;
        }

        void Awake()
        {
            m_ButtonInfo = new Dictionary<ActionButtonType, ActionButtonInfo>()
            {
                // [ActionButtonType.BasicAction] = new ActionButtonInfo(ActionButtonType.BasicAction, m_BasicActionButton, this),
                // [ActionButtonType.Special1] = new ActionButtonInfo(ActionButtonType.Special1, m_SpecialAction1Button, this),
                // [ActionButtonType.Special2] = new ActionButtonInfo(ActionButtonType.Special2, m_SpecialAction2Button, this),
                // [ActionButtonType.Special3] = new ActionButtonInfo(ActionButtonType.Special3, m_SpecialAction3Button, this),
                // [ActionButtonType.Special4] = new ActionButtonInfo(ActionButtonType.Special4, m_SpecialAction4Button, this),
                // [ActionButtonType.EmoteBar] = new ActionButtonInfo(ActionButtonType.EmoteBar, m_EmoteBarButton, this),
            };

            ClientPlayerAvatar.LocalClientSpawned += RegisterInputSender;
            ClientPlayerAvatar.LocalClientDespawned += DeregisterInputSender;
        }

        void OnEnable()
        {
            foreach (ActionButtonInfo buttonInfo in m_ButtonInfo.Values)
            {
                buttonInfo.RegisterEventHandlers();
            }
        }

        void OnDisable()
        {
            foreach (ActionButtonInfo buttonInfo in m_ButtonInfo.Values)
            {
                buttonInfo.UnregisterEventHandlers();
            }
        }

        void OnDestroy()
        {
            DeregisterInputSender();

            ClientPlayerAvatar.LocalClientSpawned -= RegisterInputSender;
            ClientPlayerAvatar.LocalClientDespawned -= DeregisterInputSender;
        }

        void Update()
        {
            if (Input.GetKeyUp(KeyCode.Alpha4))
            {
                m_ButtonInfo[ActionButtonType.EmoteBar].Button.OnPointerUpEvent.Invoke();
            }
        }

        void OnButtonClickedDown(ActionButtonType buttonType)
        {
            if (buttonType == ActionButtonType.EmoteBar)
            {
                return; // this is the "emote" button; we won't do anything until they let go of the button
            }

            if (m_InputSender == null)
            {
                //nothing to do past this point if we don't have an InputSender.
                return;
            }

            // send input to begin the action associated with this button
            m_InputSender.RequestAction(m_ButtonInfo[buttonType].CurAction.ActionID, SkillTriggerStyle.UI);
        }

        void OnButtonClickedUp(ActionButtonType buttonType)
        {
            if (buttonType == ActionButtonType.EmoteBar)
            {
                m_EmotePanel.SetActive(!m_EmotePanel.activeSelf);
                return;
            }

            if (m_InputSender == null)
            {
                //nothing to do past this point if we don't have an InputSender.
                return;
            }

            // send input to complete the action associated with this button
            m_InputSender.RequestAction(m_ButtonInfo[buttonType].CurAction.ActionID, SkillTriggerStyle.UIRelease);
        }

        void UpdateActionButton(ActionButtonInfo buttonInfo, Action action, bool isClickable = true)
        {
            // first find the info we need (sprite and description)
            Sprite sprite = null;
            string description = "";

            if (action != null)
            {
                sprite = action.Config.Icon;
                description = action.Config.Description;
            }

            // set up UI elements appropriately
            if (sprite == null)
            {
                buttonInfo.Button.gameObject.SetActive(false);
            }
            else
            {
                buttonInfo.Button.gameObject.SetActive(true);
                buttonInfo.Button.interactable = isClickable;
                buttonInfo.Button.image.sprite = sprite;
                buttonInfo.Tooltip.SetText(description);
            }

            // store the action type so that we can retrieve it in click events
            buttonInfo.CurAction = action;
        }
    }
}