using System;
using JetBrains.Annotations;
using NaughtyAttributes;
using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;

namespace EscapeRoomEngine.Desert.Runtime
{
    public enum ButtonEventType
    {
        Pressed, Released, Activated, Deactivated
    }

    public static class ButtonEventExtensions
    {
        public static string Description(this ButtonEventType type, Button button)
        {
            var action = type switch
            {
                ButtonEventType.Pressed => "pressed",
                ButtonEventType.Released => "released",
                ButtonEventType.Activated => "activated",
                ButtonEventType.Deactivated => "deactivated",
                _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
            };

            return $"{button} was {action}";
        }
    }

    public delegate void ButtonEventHandler(Button source, ButtonEventType e);
    
    /// <summary>
    /// A general component for buttons that handles button events.
    /// </summary>
    public class Button : MonoBehaviour
    {
        public event ButtonEventHandler ButtonEvent;

        /// <summary>
        /// Whether this button accepts input.
        /// </summary>
        [ShowNativeProperty]
        protected bool Active
        {
            get => _active;
            set
            {
                var previous = _active;
                _active = value;

                if (previous != _active)
                {
                    OnButtonEvent(_active ? ButtonEventType.Activated : ButtonEventType.Deactivated);
                }
            }
        }

        /// <summary>
        /// Whether this button is currently pressed.
        /// </summary>
        [ShowNativeProperty]
        protected bool Pressed
        {
            get => _pressed;
            set
            {
                var previous = _pressed;
                _pressed = Active && value;

                if (previous != _pressed)
                {
                    OnButtonEvent(_pressed ? ButtonEventType.Pressed : ButtonEventType.Released);
                }
            }
        }

        private bool _active = true, _pressed;

        protected virtual void Start()
        {
            ButtonEvent += (_, type) =>
            {
                if (type == ButtonEventType.Deactivated)
                {
                    Pressed = false; // release button if it is deactivated
                }
            };
        }

        private void OnButtonEvent(ButtonEventType type)
        {
            Logger.Log(type.Description(this), LogType.PuzzleDetail);
            
            ButtonEvent?.Invoke(this, type);
        }

        #region Debug Buttons

        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Enable() => Active = true;
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Disable() => Active = false;
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Press() => Pressed = true;
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Release() => Pressed = false;
        [UsedImplicitly]
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void PressAndRelease()
        {
            Press();
            Release();
        }
        
        #endregion

        public override string ToString()
        {
            return name;
        }
    }
}