using System;
using UnityEngine;
using Unity.Properties;

namespace Unity.Behavior
{
    [Serializable, GeneratePropertyBag]
    [NodeDescription(
        name: "Set Scale by Exponetial",
        story: "Set [Transform] scale to [ScaleValue] by [Exponetial]",
        category: "Action/Transform",
        description: "Sets the transform's scale to a specific value by Exponetial to scale over time.",
        id: "34d1c0b06985411bab6710a384d6da5d")]
    internal partial class SetScaleOverTimeAction : Action
    {
        [SerializeReference] public BlackboardVariable<Transform> Transform;
        [SerializeReference] public BlackboardVariable<Vector3> ScaleValue = new BlackboardVariable<Vector3>(Vector3.one);
        [SerializeReference] public BlackboardVariable<float> Exponetial;

        [SerializeReference] public BlackboardVariable<bool> WaitForComplete = new BlackboardVariable<bool>(true);
        [SerializeReference] public BlackboardVariable<bool> UseLocalScale = new BlackboardVariable<bool>(false);
        [SerializeReference, Tooltip("Node return success if Scale is off set within this mark\n" +
            "Value is set to target value")] public BlackboardVariable<float> SuccessOffset = new BlackboardVariable<float>(0f);

        [CreateProperty] private Vector3 m_StartScale;
        private Vector3 m_EndScale;

        protected override Status OnStart()
        {
            if (Transform.Value == null)
            {
                LogFailure("No Transform set.");
                return Status.Failure;
            }

            if (UseLocalScale.Value)
                m_StartScale = Transform.Value.localScale;
            else
                m_StartScale = Transform.Value.lossyScale;

            m_EndScale = ScaleValue.Value;

            return Verify(m_StartScale);
        }

        protected override Status OnUpdate()
        {
            Vector3 nextValue = Vector3.Lerp(m_StartScale, m_EndScale, 1f - Mathf.Exp(-Exponetial * Time.deltaTime));

            if (UseLocalScale.Value)
            {
                Transform.Value.localScale = nextValue;
                m_StartScale = Transform.Value.localScale;
            }
            else
            {
                if (Transform.Value.parent)
                {
                    Vector3 parentLossyScale = Transform.Value.parent.lossyScale;
                    Transform.Value.localScale = new Vector3(
                        nextValue.x / parentLossyScale.x,
                        nextValue.y / parentLossyScale.y,
                        nextValue.z / parentLossyScale.z);
                }
                else
                    Transform.Value.localScale = nextValue;

                m_StartScale = Transform.Value.lossyScale;
            }

            return Verify(nextValue);
        }

        protected override void OnDeserialize()
        {
            // Only target to reduce serialization size.
            m_EndScale = ScaleValue.Value;
        }

        Status Verify(Vector3 currentScale)
        {
            if (currentScale == m_EndScale)
                return Status.Success;

            if (Vector3.Distance(currentScale, m_EndScale) <= SuccessOffset)
            {
                if (UseLocalScale.Value)
                    Transform.Value.localScale = m_EndScale;
                else
                {
                    if (Transform.Value.parent)
                    {
                        Vector3 parentLossyScale = Transform.Value.parent.lossyScale;
                        Transform.Value.localScale = new Vector3(
                            m_EndScale.x / parentLossyScale.x,
                            m_EndScale.y / parentLossyScale.y,
                            m_EndScale.z / parentLossyScale.z);
                    }
                    else
                        Transform.Value.localScale = m_EndScale;
                }
                return Status.Success;
            }

            if (WaitForComplete.Value)
                return Status.Running;
            else return Status.Waiting;
        }
    }
}
