using System;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerAnimation : MonoBehaviour
{
    public static PlayerAnimation Instance { get; private set; }

    [System.Serializable]
    public class References
    {
        public Animator Anim;
    }
    
    [System.Serializable]
    private class PlayerAnimationStateMapper
    {
        public Player.PlayerState PlayerState;
        public string AnimatorState;
        public string BlockingState;
        public string Trigger;
    }

    [SerializeField] private References _references;
    [SerializeField, ReadOnly] private PlayerAnimationStateMapper[] _stateMapper;

    void OnValidate()
    {
        Init();
    }

    void Awake()
    {
        Init();

        if (!Instance)
            Instance = this;
    }

    void LateUpdate()
    {
        UpdateAnimation();
    }

    private void Init()
    {
        _stateMapper = new PlayerAnimationStateMapper[9];
        int i = 0;

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Idle,
            AnimatorState = "move",
            BlockingState = "",
            Trigger = "trigger_move",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Moving,
            AnimatorState = "move",
            BlockingState = "",
            Trigger = "trigger_move",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Jumping,
            AnimatorState = "jump",
            BlockingState = "fall",
            Trigger = "trigger_jump",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Dashing,
            AnimatorState = "dash",
            BlockingState = "",
            Trigger = "trigger_dash",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Falling,
            AnimatorState = "fall",
            BlockingState = "jump",
            Trigger = "trigger_fall",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Stunned,
            AnimatorState = "stun",
            BlockingState = "",
            Trigger = "trigger_stun",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Dead,
            AnimatorState = "eliminate",
            BlockingState = "",
            Trigger = "trigger_eliminate",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Loser,
            AnimatorState = "lose",
            BlockingState = "",
            Trigger = "trigger_lose",
        };

        _stateMapper[i++] = new PlayerAnimationStateMapper()
        {
            PlayerState = Player.PlayerState.Winner,
            AnimatorState = "win",
            BlockingState = "",
            Trigger = "trigger_win",
        };
    }

    private void UpdateAnimation()
    {
        if (!Player.Instance)
            return;

        if (_references.Anim.IsInTransition(0))
            return;

        AnimatorStateInfo currentState = _references.Anim.GetCurrentAnimatorStateInfo(0);
        AnimatorStateInfo nextState = _references.Anim.GetNextAnimatorStateInfo(0);

        PlayerAnimationStateMapper currentStateMapper = _stateMapper.FirstOrDefault(m => m.PlayerState == Player.Instance.State.CurrentState);

        if (currentStateMapper != null &&
            !currentState.IsName(currentStateMapper.AnimatorState) &&
            !nextState.IsName(currentStateMapper.AnimatorState) &&
            !currentState.IsName(currentStateMapper.BlockingState))
        {
            _references.Anim.SetTrigger(currentStateMapper.Trigger);
        }

        switch (Player.Instance.State.CurrentState)
        {
            case Player.PlayerState.Idle:
            case Player.PlayerState.Moving:
                _references.Anim.SetFloat("move", Player.Instance.State.HorizontalVelocity.magnitude);
                _references.Anim.SetFloat("run", Mathf.InverseLerp(0, .8f, Player.Instance.State.Running));
                _references.Anim.SetFloat("wall", Player.Instance.State.AnimationCenter * .5f + .5f);
                break;
        }
    }
}
