using System;
using System.Threading.Tasks;
using Godot;

namespace KarrotStarterTemplate.Common.StateMachines.Custom.PlayerStateMachines;

public enum ERootMotionAnimationDirection
{
    Character,
    Camera,
    Input,
}

public partial class RootMotionBasePlayerState : BasePlayerState
{
    [Export] public ERootMotionAnimationDirection animationDirectionType { get; set; } =
        ERootMotionAnimationDirection.Input;
    [Export] public bool updateDirectionOnProcess { get; set; }
    [Export] public ERootMotionAnimationDirection noInputDirectionTypeFallback { get; set; } =
        ERootMotionAnimationDirection.Camera;


    private Vector2 _blendPosition;
    private Vector3 _velocity = Vector3.Zero;
    private Vector3 _animationDirection = Vector3.Zero;

    protected override Task OnEnter()
    {
        _animationDirection = GetRootMotionDirection();
        return base.OnEnter();
    }

    protected override void OnStateProcess(double delta)
    {

        _velocity = animationTree.GetRootMotionPosition();
        base.OnStateProcess(delta);
    }

    protected override void OnStatePhysicsProcess(double delta)
    {
        if (updateDirectionOnProcess)
        {
            _animationDirection = GetRootMotionDirection();
        }

        if (_animationDirection != Vector3.Zero)
        {
            characterBody.GlobalRotation = new Vector3(0,
                Mathf.LerpAngle(characterBody.GlobalRotation.Y,
                    Mathf.Atan2(_animationDirection.X, _animationDirection.Z), 10 * (float)delta), 0);
        }


        Quaternion rotationQuaternion = characterBody.Transform.Basis.GetRotationQuaternion();

        characterBody.Velocity = rotationQuaternion.Normalized() * _velocity / (float)delta;
        characterBody.MoveAndSlide();
        _velocity = Vector3.Zero;
    }

    private Vector3 GetRootMotionDirection()
    {
        ERootMotionAnimationDirection temp = animationDirectionType;
        if (temp == ERootMotionAnimationDirection.Input && package.GetVector3("move_direction").IsZeroApprox())
        {
            temp = noInputDirectionTypeFallback;
        }


        switch (temp)
        {
            case ERootMotionAnimationDirection.Character:
                return Vector3.Zero;
            case ERootMotionAnimationDirection.Camera:
                return (cameraController.camera.GlobalBasis.Z);
            case ERootMotionAnimationDirection.Input:
                return (cameraController.camera.GlobalBasis.Z * package.GetVector3("move_direction").Y) +
                       cameraController.camera.GlobalBasis.X * package.GetVector3("move_direction").X;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}