using System;
using System.Linq;
using System.Reflection;
using Sirenix.OdinInspector;
using UnityEngine;

namespace VOID.Generic
{
    [Serializable]
    [HideReferenceObjectPicker]
    public class MultiSourceWrapper<T> where T : class
    {
        /// <summary>
        /// Returns value from given source:
        /// - From inline object
        /// - From selected ScriptableObject (that have field "value")
        /// </summary>
        public T value => _usageType switch
        {
            MultiSourceUsageType.None => null,
            MultiSourceUsageType.Inline => _value,
            MultiSourceUsageType.ScriptableObject => _valueFromScriptableObject,
            _ => throw new ArgumentOutOfRangeException()
        };
        
        [SerializeField]
        [EnumToggleButtons]
        [OnValueChanged(nameof(OnUsageTypeChange))]
        [HideLabel]
        private MultiSourceUsageType _usageType;
        
        [SerializeField]
        [ShowIf(nameof(_usageType), MultiSourceUsageType.Inline)]
        [HideLabel]
        private T _value;
        
        [SerializeField]
        [ShowIf(nameof(_usageType), MultiSourceUsageType.ScriptableObject)]
        [OnValueChanged(nameof(OnScriptableObjectSelected))]
        [HideLabel]
        private ScriptableObject _scriptableObject;

        private T _valueFromScriptableObject;

        private void OnScriptableObjectSelected()
        {
            var field = _scriptableObject.GetType()
                .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .FirstOrDefault(field => field.Name == "value" && field.FieldType == typeof(T));

            _valueFromScriptableObject = field == null ? default : field.GetValue(_scriptableObject) as T;
        }

        private void OnUsageTypeChange()
        {
            switch (_usageType)
            {
                case MultiSourceUsageType.None:
                    _value = null;
                    _scriptableObject = null;
                    _valueFromScriptableObject = null;
                    break;
                case MultiSourceUsageType.Inline:
                    _scriptableObject = null;
                    _valueFromScriptableObject = null;
                    break;
                case MultiSourceUsageType.ScriptableObject:
                    _value = null;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}