using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using static Cinemachine.DocumentationSortingAttribute;

namespace Marro.PacManUdon
{
    public enum PoleStrechLevels
    {
        None = 0,
        Strech1 = 1,
        Strech2 = 2,
        Strech3 = 3,
        Separated = 4,
    }
    
    [RequireComponent(typeof(Animator))]
    public class Intermission2Pole : SyncedObject
    {
        Animator _animator;

        GameManager _gameManager;
        Ghost _ghost;

        PoleStrechLevels _lastUpdate;

        const float Strech1Distance = 0f;
        const float Strech2Distance = 0.250f;
        const float Strech3Distance = 0.625f;
        const float SeparatedDistance = 1f;

        public void Initialize(GameManager gameManager, Ghost ghost)
        {
            _ghost = ghost;
            _gameManager = gameManager;
            _animator = GetComponent<Animator>();
            SetActive(false); // Should only activate for intermission 2
            Reset();
        }

        public void Reset()
        {
            _lastUpdate = PoleStrechLevels.None;
            SetStrechLevel(PoleStrechLevels.None);
        }

        public void SetActive(bool isActive)
        {
            //Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) SetActive {isActive}.");
            gameObject.SetActive(isActive);
        }

        public override void SyncedUpdate()
        {
            if (!_ghost.gameObject.activeInHierarchy)
            {
                return;
            }

            var ghostDistance = -(_ghost.GetPosition().x - GetPosition().x);
            // Debug.Log(ghostDistance);

            if (ghostDistance < 0)
            {
                return;
            }

            var level = PoleStrechLevels.None;

            if (ghostDistance >= SeparatedDistance)
            {
                level = PoleStrechLevels.Separated;
            }
            else if (ghostDistance >= Strech3Distance)
            {
                level = PoleStrechLevels.Strech3;
            }
            else if (ghostDistance >= Strech2Distance)
            {
                level = PoleStrechLevels.Strech2;
            }
            else if (ghostDistance >= Strech1Distance)
            {
                level = PoleStrechLevels.Strech1;
            }

            ProcessDistanceUpdate(level);
        }

        private void ProcessDistanceUpdate(PoleStrechLevels level)
        {
            if ((int)_lastUpdate >= (int)level)
            {
                return;
            }

            _lastUpdate = level;

            if (level != PoleStrechLevels.Separated) // This one is done later via the timed procedure
            {
                SetStrechLevel(level);
            }
            else
            {
                // Align ghost nicely with pole :)
                _ghost.SetPosition(new Vector2(GetPosition().x - SeparatedDistance, _ghost.GetPosition().y));
            }

            if (level == PoleStrechLevels.Strech1 || level == PoleStrechLevels.Separated) // Step forward timed procedure
            {
                _gameManager.Intermission2PoleUpdate();
            }
        }

        public void SetStrechLevel(PoleStrechLevels level)
        {
            //Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) Set strech level to {level}.");
            _animator.SetFloat("Strech", GetAnimatorValueForStrechLevel(level));
        }

        private float GetAnimatorValueForStrechLevel(PoleStrechLevels level)
        {
            switch (level)
            {
                case PoleStrechLevels.None:
                    return 0f;
                case PoleStrechLevels.Strech1:
                    return 1f;
                case PoleStrechLevels.Strech2:
                    return 2f;
                case PoleStrechLevels.Strech3:
                    return 3f;
                case PoleStrechLevels.Separated: 
                    return 4f;
                default:
                    Debug.LogError("Invalid pole strech level!");
                    return 0f;
            }
        }

        public Vector2 GetPosition()
        {
            return (Vector2)transform.localPosition;
        }
    }
}