using System;
using UniRx;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;

public enum ThermalCondition
{
    Cold,
    Yellow,
    Hot
}

public class ThermalControl : MonoBehaviour
{
    [SerializeField] private PanController panController;
    [SerializeField] private ThermoMeter thermoMeter;
    [Space]
    [SerializeField, ReadOnly] private float temperature;
    [SerializeField, ReadOnly] private float temperatureSpeed;
    [SerializeField, ReadOnly] private float panSpeed;
    [Space]
    [Header("フライパン感度/デッドゾーン")]
    [SerializeField] private float panFactor = .5f;
    [SerializeField] private float deadZone = .1f;
    [Header("熱くなるスピード/冷えるスピードの最大値/加速")]
    [SerializeField] private float baseIncreaseSpeed = .03f;
    [SerializeField] private float coldIncreaseSpeed = .03f;
    [SerializeField] private float hotIncreaseSpeed = .07f;
    [SerializeField] private float maxDecreaseSpeed = .03f;
    [Space]
    [Header("ゲージ調整")]
    [Range(0f, .8f)]
    [SerializeField] private float yellowSize;
    [Range(0f, .5f)]
    [SerializeField] private float randomRange;
    private float coldValue;
    private float hotValue;
    
    private float increaseDuration = 0.3f;
    private float decreaseDuration = 0.1f;
    private bool isMove;

    public IReadOnlyReactiveProperty<ThermalCondition> Condition => condition;
    private readonly ReactiveProperty<ThermalCondition> condition = new ReactiveProperty<ThermalCondition>(ThermalCondition.Cold); 

#if DEVELOPMENT_BUILD || UNITY_EDITOR
    [SerializeField] private Slider factorSlider;
    [SerializeField] private Slider deadZoneSlider;
    [SerializeField] private Slider baseSpeedSlider;
    [SerializeField] private Slider coldSpeedSlider;
    [SerializeField] private Slider hotSpeedSlider;
    [SerializeField] private Slider decreaseSpeedSlider;
    [SerializeField] private Slider yellowSizeSlider;
    [SerializeField] private Slider randomRangeSlider;
#endif

    private void Start()
    {
        condition.AddTo(this);
#if UNITY_EDITOR
        condition.Subscribe(x =>
        {
            Debug.Log($"cond :{x} temp: {temperature}");
        }).AddTo(this);
#endif

#if DEVELOPMENT_BUILD || UNITY_EDITOR
        factorSlider.value = panFactor;
        deadZoneSlider.value = deadZone;
        baseSpeedSlider.value = baseIncreaseSpeed;
        coldSpeedSlider.value = coldIncreaseSpeed;
        hotSpeedSlider.value = hotIncreaseSpeed;
        decreaseSpeedSlider.value = maxDecreaseSpeed;
        yellowSizeSlider.value = yellowSize;
        randomRangeSlider.value = randomRange;
        factorSlider.OnValueChangedAsObservable().Subscribe(x => panFactor = x).AddTo(this);
        deadZoneSlider.OnValueChangedAsObservable().Subscribe(x => deadZone = x).AddTo(this);
        baseSpeedSlider.OnValueChangedAsObservable().Subscribe(x => baseIncreaseSpeed = x).AddTo(this);
        coldSpeedSlider.OnValueChangedAsObservable().Subscribe(x => coldIncreaseSpeed = x).AddTo(this);
        hotSpeedSlider.OnValueChangedAsObservable().Subscribe(x => hotIncreaseSpeed = x).AddTo(this);
        decreaseSpeedSlider.OnValueChangedAsObservable().Subscribe(x => maxDecreaseSpeed = x).AddTo(this);
        yellowSizeSlider.OnValueChangedAsObservable().Subscribe(x => yellowSize = x).AddTo(this);
        randomRangeSlider.OnValueChangedAsObservable().Subscribe(x => randomRange = x).AddTo(this);
#endif
    }

    private void Update()
    {
        if (!isMove)
        {
            return;
        }
        panSpeed = panController.Velocity.magnitude * panFactor;
        if (panSpeed > deadZone)
        {
            temperatureSpeed = Mathf.Lerp(temperatureSpeed, -Mathf.Min(panSpeed, maxDecreaseSpeed), Time.deltaTime / decreaseDuration);
        }
        else
        {
            temperatureSpeed = Mathf.Lerp(temperatureSpeed, GetIncreaseSpeed(condition.Value), Time.deltaTime / increaseDuration);
            if (condition.Value == ThermalCondition.Hot)
            {
                temperatureSpeed = Mathf.Lerp(temperatureSpeed, hotIncreaseSpeed, Time.deltaTime / increaseDuration);
            }
            else
            {
                temperatureSpeed = Mathf.Lerp(temperatureSpeed, baseIncreaseSpeed, Time.deltaTime / increaseDuration);
            }
        }
        temperature = Mathf.Clamp01(temperature + temperatureSpeed * Time.deltaTime);
        thermoMeter.SetValue(temperature);
        condition.Value = GetCondition();
    }

    private float GetIncreaseSpeed(ThermalCondition thermalCondition)
    {
        switch (thermalCondition)
        {
            case ThermalCondition.Cold:
                return coldIncreaseSpeed;
                break;
            case ThermalCondition.Yellow:
                return baseIncreaseSpeed;
                break;
            case ThermalCondition.Hot:
                return hotIncreaseSpeed;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(thermalCondition), thermalCondition, null);
        }
    }

    private ThermalCondition GetCondition()
    {
        if (temperature <= coldValue)
        {
            return ThermalCondition.Cold;
        }
        if (temperature >= hotValue)
        {
            return ThermalCondition.Hot;
        }
        return ThermalCondition.Yellow;
    }

    private void SetMeterValue(bool isTutorial = false)
    {
        float yellowPos;
        float halfYellowSize;
        // チュートリアル時中央固定
        if (isTutorial)
        {
            yellowPos = .5f;
            halfYellowSize = .4f / 2;
        }
        else
        {
            yellowPos = Random.Range(-randomRange, randomRange) + .5f;
            halfYellowSize = yellowSize / 2;
        }
        coldValue = Mathf.Max(0f, yellowPos - halfYellowSize);
        hotValue = Mathf.Min(1f, yellowPos + halfYellowSize);
    }

    public void ResetMeter(bool isTutorial = false)
    {
        isMove = false;
        temperatureSpeed = 0f;
        temperature = 0f;
        thermoMeter.SetValue(temperature);
        SetMeterValue(isTutorial);
        thermoMeter.SetScale(coldValue, hotValue);
        panController.ResetTargetPosition();
        panController.enabled = true;
    }

    public void StartMeter()
    {
        isMove = true;
        condition.SetValueAndForceNotify(GetCondition());
    }

    public void StopMeter()
    {
        isMove = false;
        panController.enabled = false;
    }

    public void SetPanData(PanData panData)
    {
        panFactor = panData.panFactor;
        deadZone = panData.deadZone;
        baseIncreaseSpeed = panData.baseIncreaseSpeed;
        coldIncreaseSpeed = panData.coldIncreaseSpeed;
        hotIncreaseSpeed = panData.hotIncreaseSpeed;
        maxDecreaseSpeed = panData.maxDecreaseSpeed;
        yellowSize = panData.yellowSize;
        randomRange = panData.randomRange;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
        factorSlider.value = panFactor;
        deadZoneSlider.value = deadZone;
        baseSpeedSlider.value = baseIncreaseSpeed;
        coldSpeedSlider.value = coldIncreaseSpeed;
        hotSpeedSlider.value = hotIncreaseSpeed;
        decreaseSpeedSlider.value = maxDecreaseSpeed;
        yellowSizeSlider.value = yellowSize;
        randomRangeSlider.value = randomRange;
#endif
    }
}
