using System;
using UnityEngine;
using UnityEngine.InputSystem;

namespace KitsuneCafe.SOAP
{
    public class ReactiveInputAction<T> : ScriptableObject where T : struct
    {
        [SerializeField]
        private ReactiveEvent<T> started;

        [SerializeField]
        private ReactiveEvent<T> performed;

        [SerializeField]
        private ReactiveEvent<T> canceled;

        [SerializeField]
        private ReactiveEvent<T> waiting;

        [SerializeField]
        private ReactiveEvent<T> disabled;

        [SerializeField]
        private ReactiveValue<T> reactiveValue;

        [SerializeField]
        private ReactiveValue<bool> valueAsButton;

        public virtual void Handle(InputAction.CallbackContext context)
        {
            T value = context.ReadValue<T>();
            Raise(context.phase, value);
            UpdateValue(reactiveValue, value);
            UpdateValue(valueAsButton, context);
        }

        protected void UpdateValue(ReactiveValue<T> property, T value)
        {
            if (property != null)
            {
                property.Value = value;
            }
        }

        protected void UpdateValue(ReactiveValue<T> property, InputAction.CallbackContext context)
        {
            if (property != null)
            {
                property.Value = context.ReadValue<T>();
            }
        }

        protected void UpdateValue(ReactiveValue<bool> property, InputAction.CallbackContext context)
        {
            if (property != null)
            {
                property.Value = context.ReadValueAsButton();
            }
        }

        public virtual void UpdateValue(InputAction.CallbackContext context)
        {
            UpdateValue(reactiveValue, context.ReadValue<T>());
            UpdateValue(valueAsButton, context);
        }

        protected virtual void Raise(InputActionPhase phase, T value)
        {
            switch (phase)
            {
                case InputActionPhase.Started:
                    TryRaise(started, value);
                    break;
                case InputActionPhase.Performed:
                    TryRaise(performed, value);
                    break;
                case InputActionPhase.Canceled:
                    TryRaise(canceled, value);
                    break;
                case InputActionPhase.Waiting:
                    TryRaise(waiting, value);
                    break;
                case InputActionPhase.Disabled:
                    TryRaise(disabled, value);
                    break;
                default:
                    throw new ArgumentException($"{phase} is not a valid InputActionPhase");
            }
        }

        public virtual void Raise(InputAction.CallbackContext context)
        {
            Raise(context.phase, context.ReadValue<T>());
        }

        private void TryRaise(ReactiveEvent<T> evt, T value)
        {
            if (evt != null)
            {
                evt.Raise(value);
            }
        }
    }
}
