#define RECORDING_DEMO

namespace Marro.PacManUdon
{
    using UdonSharp;
    using System;
    using UnityEngine;
    using VRC.SDKBase;
    using VRC.Udon;
    using VRC.SDK3.Components;
    using VRC.Udon.Common.Interfaces;

    enum PacManGhostType
    {
        Caught,
        Scared,
        ScaredWhite,
        Blinky,
        Pinky,
        Inky,
        Clyde,
        Special,
    }

    public enum PacManGhostState
    {
        Normal,
        CaughtScore,
        Returning,
        Entering,
        Home,
        Exiting
    }

    public enum PacManGhostStartState
    {
        Outside,
        TargetingIdlePosition1,
        TargetingIdlePosition2
    }

    public class Ghost : GridMover
    {
        [SerializeField] private PacManGhostType ghostType;
        [SerializeField] private PacManGhostStartState startState;

        private GhostManager ghostManager;
        private Animator animator;
        private new Renderer renderer;
        private PacMan pacMan;
        private Ghost blinky;
        private ScoreBonusDisplay scoreBonusDisplay;

        private Vector3 startPosition;
        private Quaternion startRotation;
        private Vector3 startScale;

        private Vector2 homePosition;
        private Vector2 idlePosition1;
        private Vector2 idlePosition2;
        private Vector2 cornerPosition;

        private bool kinematic;

        private bool horizontalOnly;
        private int housePelletCounterLimit;

        private bool faceInStartingDirectionUntilUnfrozen;
        private bool specialLook;

        private bool followingPredefinedPath;
        private Vector2[] predefinedPath;
        private int predefinedPathIndex;

        [UdonSynced] int rngState;
        [UdonSynced] private Vector2 syncedPosition;
        [UdonSynced] private float speed;
        [UdonSynced] private Vector2 direction;
        [UdonSynced] private Vector2 target;
        [UdonSynced] private bool offGrid;
        [UdonSynced] private bool inTunnel;
        [UdonSynced] private PacManGhostState ghostState;
        [UdonSynced] private bool isScared;
        [UdonSynced] private bool scattering;
        [UdonSynced] private PacManGhostFrozenState frozenState;
        [UdonSynced] private bool hideUntilUnfrozen;
        [UdonSynced] private int housePelletCounter;
        [UdonSynced] private bool housePelletCounterActive;
        [UdonSynced] private bool turnAroundSoon;

        public void Initialize(PacMan pacMan, Ghost blinky, Vector2 homePosition, Vector2 idlePosition1, Vector2 idlePosition2, Vector2 cornerPosition)
        {
            ghostManager = transform.parent.GetComponent<GhostManager>();
            animator = GetComponent<Animator>();
            renderer = GetComponent<Renderer>();

            this.pacMan = pacMan;
            this.blinky = blinky;
            this.homePosition = homePosition;
            this.idlePosition1 = idlePosition1;
            this.idlePosition2 = idlePosition2;
            this.cornerPosition = cornerPosition;

            scoreBonusDisplay = transform.Find("ScoreBonusDisplay").gameObject.GetComponent<ScoreBonusDisplay>();
            scoreBonusDisplay.Initialize();
            startPosition = transform.localPosition;
            startRotation = transform.localRotation;
            startScale = transform.localScale;

            frozenState = PacManGhostFrozenState.Frozen;
            // Debug.Log($"{gameObject} Begin localScale = {initialScale}");
        }

        public void Reset()
        {
            // Debug.Log($"{gameObject} Reset!");
            transform.localPosition = startPosition;
            transform.localRotation = startRotation;
            transform.localScale = startScale;
            // Debug.Log($"{gameObject} Reset localScale = {transform.localScale}");

            if (startState == PacManGhostStartState.Outside)
            {
                ghostState = PacManGhostState.Exiting;
                OffGridTargetReached();
            }
            else
            {
                if (startState == PacManGhostStartState.TargetingIdlePosition1)
                {
                    SetOffGridTarget(idlePosition1, false);
                }
                ghostState = PacManGhostState.Entering;
                OffGridTargetReached();
            }

            isScared = false;
            inTunnel = false;
            kinematic = false;
            followingPredefinedPath = false;
            turnAroundSoon = false;
            specialLook = false;

            rngState = 1;
            UpdateSpeed();

            faceInStartingDirectionUntilUnfrozen = true;
            UpdateAnimator();
            RequestSerialization();

            // Debug.Log($"{gameObject} reset with state: {state}, target: {target}, offGrid: {offGrid}");
        }

        void FixedUpdate()
        {
            if (ghostType == PacManGhostType.Blinky)
            {
                // ghostManager.gameStateManager.statusDisplay.SetDebugText(2, $"{turnAroundSoon}");
            }
            if (frozenState == PacManGhostFrozenState.Frozen ||
            (frozenState == PacManGhostFrozenState.FrozenIfNotCaught && ((ghostState != PacManGhostState.Returning && ghostState != PacManGhostState.Entering) || hideUntilUnfrozen)))
            {
                return;
            }

            Vector2 position = GetPosition();
            Vector2 nextPosition = GridMoverTools.GetNextPosition(position, direction, speed);

            nextPosition = ProcessNextPosition(position, nextPosition);

            SetPosition(nextPosition);
        }

        private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition)
        {
            if (turnAroundSoon && ghostState == PacManGhostState.Normal
                && GridMoverTools.CrossesTileBorder(position, nextPosition, direction.x != 0, direction.y != 0))
            {
                // Debug.Log($"{gameObject} turned around");
                SetDirection(direction * -1);
                turnAroundSoon = false;
            }

            if (kinematic)
            {
                return nextPosition;
            }

            if (offGrid || ghostState == PacManGhostState.Returning)
            {
                bool XAxisAlligned = CheckAndAllignToTargetX(position, nextPosition, target);
                bool YAxisAlligned = CheckAndAllignToTargetY(position, nextPosition, target);
                if (offGrid)
                {
                    if (XAxisAlligned)
                    {
                        nextPosition.x = target.x;
                    }
                    if (YAxisAlligned)
                    {
                        nextPosition.y = target.y;
                    }
                }
                if (XAxisAlligned && YAxisAlligned)
                {
                    if (!offGrid)
                    {
                        nextPosition = target;
                    }
                    OffGridTargetReached();
                    // Debug.Log($"{gameObject} reached offGridTarget {target} at position {position}, new state {state}, offGrid is now {offGrid}, new direction: {direction}");
                }
                if ((XAxisAlligned || YAxisAlligned) && offGrid)
                {
                    SetDirection(GetOffGridDirectionToTarget(nextPosition, target, direction));
                    // Debug.Log($"{gameObject} Alligned X Axis: {XAxisAlligned}, Y Axis: {YAxisAlligned} with position: {position}, new nextPosition: {nextPosition}, new target: {target}, now moving in direction {direction}");
                    // nextPosition = GridMover.GetNextPosition(position, direction, speed);
                }
            }
            if (!offGrid && followingPredefinedPath)
            {
                nextPosition = ProcessPredefinedPath(position, nextPosition);
            }
            else if (!offGrid && GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, direction.y != 0))
            {
                Vector2 gridPosition = GridMoverTools.PositionToGrid(position);
                Vector2[] availableDirections = GetAvailableDirections(gridPosition, direction * -1);
                if (availableDirections.Length > 1)
                {
                    target = GetGridTarget(gridPosition);
                    SetDirection(GetGridDirectionToTargetGreedy(availableDirections, gridPosition, target));
                    nextPosition = GridMoverTools.GetNextPosition(gridPosition, direction, speed);
                    // Debug.Log($"GetNextPosition at gridPosition {gridPosition} with direction {direction} and speed {speed} gives nextPosition {nextPosition}");
                }
                else if (availableDirections.Length == 1 && availableDirections[0] != direction)
                {
                    SetDirection(availableDirections[0]);
                    nextPosition = GridMoverTools.GetNextPosition(gridPosition, direction, speed);
                }
                // Debug.Log($"{gameObject} crossed tile center {gridPosition}, new target: {target}, new direction: {direction}");
            }

            return nextPosition;
        }

        private Vector2 ProcessPredefinedPath(Vector2 position, Vector2 nextPosition)
        {
            if (GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, direction.y != 0))
            {
                // Find the next valid direction which isn't Vector2.zero
                int nextValidDirectionIndex = predefinedPathIndex;
                while (predefinedPath[nextValidDirectionIndex] == Vector2.zero)
                {
                    nextValidDirectionIndex += 1;
                }
                if (!GridMoverTools.CheckCollisionInDirection(transform, nextPosition, predefinedPath[nextValidDirectionIndex]))
                {
                    // If we're at a Vector2.zero, we skip applying the direction and only increment.
                    if (nextValidDirectionIndex == predefinedPathIndex)
                    {
                        SetDirection(predefinedPath[nextValidDirectionIndex]);
                        nextPosition = GridMoverTools.PositionToGrid(nextPosition) + direction.normalized * 0.01f;

                        // Check if we've reached the end of the path, which includes making sure the path doesn't end on Vector2.zero
                        do
                        {
                            nextValidDirectionIndex += 1;
                            if (nextValidDirectionIndex >= predefinedPath.Length)
                            {
                                followingPredefinedPath = false;
                                break;
                            }
                        } while (predefinedPath[nextValidDirectionIndex] == Vector2.zero);
                    }

                    // ghostManager.gameStateManager.statusDisplay.SetDebugText(1, predefinedPathIndex.ToString());

                    predefinedPathIndex++;
                }
            }
            return nextPosition;
        }

        Vector2 GetGridTarget(Vector2 gridPosition)
        {
            // if (followingPredefinedPath)
            // {
            //     predefinedPathIndex++;
            //     if (predefinedPathIndex >= predefinedPath.Length)
            //     {
            //         followingPredefinedPath = false;
            //     }
            //     return gridPosition + predefinedPath[predefinedPathIndex];
            // }
            if (isScared)
            {
                switch (PseudoRNG() % 4)
                {
                    default:
                        return gridPosition;
                    case 0:
                        return gridPosition + Vector2.up;
                    case 1:
                        return gridPosition + Vector2.left;
                    case 2:
                        return gridPosition + Vector2.down;
                    case 3:
                        return gridPosition + Vector2.right;
                }
            }
            switch (ghostState)
            {
                default:
                    return gridPosition;
                case PacManGhostState.Normal:
                    if (scattering)
                    {
                        return cornerPosition;
                    }
                    switch (ghostType)
                    {
                        default:
                            return gridPosition;
                        case PacManGhostType.Blinky:
                            return GridMoverTools.PositionToGrid(pacMan.transform.localPosition);
                        case PacManGhostType.Pinky:
                            return GridMoverTools.PositionToGrid(pacMan.transform.localPosition) + pacMan.GetDirection() * 4;
                        case PacManGhostType.Inky:
                            return 2 * GridMoverTools.PositionToGrid(pacMan.transform.localPosition) + 4 * pacMan.GetDirection() - GridMoverTools.PositionToGrid(blinky.transform.localPosition);
                        case PacManGhostType.Clyde:
                            if (Vector2.Distance(gridPosition, GridMoverTools.PositionToGrid(pacMan.transform.localPosition)) >= 8)
                            {
                                return GridMoverTools.PositionToGrid(pacMan.transform.localPosition);
                            }
                            else
                            {
                                // Debug.Log($"{gameObject} goes to cornerPosition {cornerPosition}");
                                return cornerPosition;
                            }
                    }
                case PacManGhostState.Returning:
                    return homePosition;
            }
        }

        bool CheckAndAllignToTargetX(Vector2 currentPosition, Vector2 nextPosition, Vector2 target)
        {
            return (currentPosition.x - target.x) * (nextPosition.x - target.x) <= 0.01;
        }

        bool CheckAndAllignToTargetY(Vector2 currentPosition, Vector2 nextPosition, Vector2 target)
        {
            return (currentPosition.y - target.y) * (nextPosition.y - target.y) <= 0.01;
        }

        void OffGridTargetReached()
        {
            switch (ghostState)
            {
                case PacManGhostState.Returning:
                    offGrid = true;
                    SetState(PacManGhostState.Entering);
                    SetOffGridTarget(idlePosition2, false);
                    break;
                case PacManGhostState.Entering:
                    offGrid = true;
                    SetState(PacManGhostState.Home);
                    ghostManager.GhostReturned();
                    if (!target.Equals(idlePosition1))
                    { // This is idlePosition1 if PacManGhostStartState == TargetingIdlePosition1
                        SetOffGridTarget(idlePosition2, false);
                    }
                    TryToExit();
                    break;
                case PacManGhostState.Home:
                    if (target.Equals(idlePosition1))
                    {
                        SetOffGridTarget(idlePosition2, false);
                    }
                    else
                    {
                        SetOffGridTarget(idlePosition1, false);
                    }
                    break;
                case PacManGhostState.Exiting:
                    offGrid = false;
                    SetState(PacManGhostState.Normal);
                    SetDirection(Vector2.left);
                    break;
            }
        }

        void SetOffGridTarget(Vector2 newTarget, bool startHorizontal)
        {
            if (startHorizontal)
            {
                SetDirection(GetOffGridDirectionToTarget(GetPosition(), newTarget, Vector2.right));
            }
            else
            {
                SetDirection(GetOffGridDirectionToTarget(GetPosition(), newTarget, Vector2.down));
            }
            // Debug.Log($"{gameObject} SetOffGridTarget with position {GetPosition()}, newTarget {newTarget}, startHorizontal {startHorizontal} resulted in direction {direction}");
            target = newTarget;
        }

        public int PseudoRNG()
        {
            rngState ^= rngState << 13;
            rngState ^= rngState >> 17;
            rngState ^= rngState << 5;
            return rngState;
        }

        Vector2[] GetAvailableDirections(Vector2 gridPosition, Vector2 discardDirection)
        {
            Vector2[] directions;
            Vector2[] availableDirections;
            if (horizontalOnly && ghostState == PacManGhostState.Normal)
            {
                directions = new Vector2[] { new Vector2(1, 0), new Vector2(-1, 0) };
                availableDirections = new Vector2[2];
            }
            else
            {
                directions = new Vector2[] { new Vector2(0, 1), new Vector2(1, 0), new Vector2(0, -1), new Vector2(-1, 0) };
                availableDirections = new Vector2[4];
            }

            int availableDirectionsNum = 0;
            for (int i = 0; i < directions.Length; i++)
            {
                if (directions[i].Equals(discardDirection) || GridMoverTools.CheckCollisionInDirection(transform, gridPosition, directions[i]))
                {
                    continue;
                }
                availableDirections[availableDirectionsNum] = directions[i];
                availableDirectionsNum++;
            }
            Vector2[] availableDirectionsTrimmed = new Vector2[availableDirectionsNum];
            // Debug.Log($"{gameObject} Number of available directions: {availableDirectionsTrimmed.Length}");
            Array.Copy(availableDirections, 0, availableDirectionsTrimmed, 0, availableDirectionsNum);

            return availableDirectionsTrimmed;
        }

        Vector2 GetGridDirectionToTargetGreedy(Vector2[] availableDirections, Vector2 gridPosition, Vector2 targetGridPosition)
        {
            Vector2 bestDirection = Vector2.zero;
            float bestDistance = float.MaxValue;
            for (int i = 0; i < availableDirections.Length; i++)
            {
                Vector2 direction = availableDirections[i];

                // Debug.Log("Evaluating direction " + direction);
                float distance = Vector2.Distance(gridPosition + direction, targetGridPosition);
                if (distance < bestDistance)
                {
                    bestDistance = distance;
                    bestDirection = direction;
                }
            }
            // Debug.Log("Closest next tile is in direction " + bestDirection);
            return bestDirection;
        }

        Vector2 GetOffGridDirectionToTarget(Vector2 position, Vector2 target, Vector2 direction)
        {
            if ((direction.x != 0 && position.x != target.x) || position.y == target.y)
            {
                // Debug.Log($"{gameObject} getOffGridDirectionToTarget with position {position}, target {target} and direction {direction} gives movement on the X axis in direction {new Vector2(target.x-position.x, 0).normalized}");
                return new Vector2(target.x - position.x, 0).normalized;
            }
            else
            {
                // Debug.Log($"{gameObject} getOffGridDirectionToTarget with position {position}, target {target} and direction {direction} gives movement on the Y axis in direction {new Vector2(0, target.y-position.y).normalized}");
                return new Vector2(0, target.y - position.y).normalized;
            }
        }

        private void UpdateAnimator()
        {
            if (!gameObject.activeInHierarchy)
                return;

            // Debug.Log($"{gameObject} UpdateAnimator with state: {ghostState}, isScared: {isScared}, direction: {direction}");
            if (specialLook)
            {
                animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Special));
            }
            else if (isScared)
            {
                float currentGhostType = animator.GetFloat("GhostType");
                if (currentGhostType > 0.5f && currentGhostType < 2.5f)
                {
                    return;
                }
                animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Scared));
            }
            else
            {
                switch (ghostState)
                {
                    default:
                    case PacManGhostState.Normal:
                    case PacManGhostState.Home:
                    case PacManGhostState.Exiting:
                        // Debug.Log($"{gameObject} Set GhostType in animator to: {GhostTypeToAnimationValue(ghostType)}");
                        animator.SetFloat("GhostType", GhostTypeToAnimationValue(ghostType));
                        break;
                    case PacManGhostState.Returning:
                    case PacManGhostState.Entering:
                        animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Caught));
                        break;
                }
            }

            if (faceInStartingDirectionUntilUnfrozen && startState == PacManGhostStartState.TargetingIdlePosition1)
            {
                animator.SetFloat("DirX", 0);
                animator.SetFloat("DirY", 1);
            }
            else if (faceInStartingDirectionUntilUnfrozen && startState == PacManGhostStartState.TargetingIdlePosition2)
            {
                animator.SetFloat("DirX", 0);
                animator.SetFloat("DirY", -1);
            }
            else if (specialLook || !direction.Equals(Vector2.zero))
            {
                animator.SetFloat("DirX", direction.x);
                animator.SetFloat("DirY", direction.y);
            }
        }

        private float GhostTypeToAnimationValue(PacManGhostType ghostType)
        {
            switch (ghostType)
            {
                default:
                    Debug.LogError("Invalid ghost animation value!");
                    return 0;
                case PacManGhostType.Caught:
                    return 0;
                case PacManGhostType.Scared:
                    return 1;
                case PacManGhostType.ScaredWhite:
                    return 2;
                case PacManGhostType.Blinky:
                    return 3;
                case PacManGhostType.Pinky:
                    return 4;
                case PacManGhostType.Inky:
                    return 5;
                case PacManGhostType.Clyde:
                    return 6;
                case PacManGhostType.Special:
                    return 7;
            }
        }

        public void UpdateSpeed()
        {
            speed = ghostManager.GetTargetSpeed(this, ghostState, isScared, inTunnel);
            // Debug.Log($"Ghost with type {ghostType} updated speed to {speed}, ghostState: {ghostState}, isScared: {isScared}, inTunnel: {inTunnel}, elroyLevel: {ghostManager.elroyLevel}");
        }

        public void ResetHousePelletCounter()
        {
            housePelletCounter = 0;
        }

        public void IncrementHousePelletCounter()
        {
            housePelletCounter++;
            TryToExit();
        }

        void TryToExit()
        {
            if ((housePelletCounterActive && housePelletCounter >= housePelletCounterLimit) || startState == PacManGhostStartState.Outside)
            {
                Release();
            }
        }

        public void Caught(int scoreBonus)
        {
            isScared = false;
            // direction = direction * -1;
            target = homePosition;
            scoreBonusDisplay.Display(scoreBonus);
            SetVisibility(false);
            SetState(PacManGhostState.CaughtScore);
        }

        public void ReturnHome()
        {
            SetVisibility(true);
            scoreBonusDisplay.Hide();
            SetState(PacManGhostState.Returning);
        }

        public void BecomeScared()
        {
            if (ghostState == PacManGhostState.Returning || ghostState == PacManGhostState.Entering)
            {
                return;
            }

            if (ghostState == PacManGhostState.Normal || ghostState == PacManGhostState.Home || ghostState == PacManGhostState.Exiting)
            {
                turnAroundSoon = true;
            }

            SetScared(true);
        }

        public void CalmDown()
        {
            if (isScared)
            {
                SetScared(false);
            }
        }

        public void Release()
        {
            // Debug.Log($"{gameObject} was released.");
            if (ghostState == PacManGhostState.Home)
            {
                // Debug.Log($"{gameObject} released.");
                SetOffGridTarget(homePosition, true);
                SetState(PacManGhostState.Exiting);
            }
        }

        public void SetState(PacManGhostState state)
        {
            ghostState = state;
            UpdateAnimator();
            UpdateSpeed();
            if (Networking.IsOwner(gameObject))
            {
                RequestSerialization();
            }
        }

        private void SetScared(bool scared)
        {
            isScared = scared;
            UpdateAnimator();
            UpdateSpeed();
            
            if (isScared)
            {
                SetWhite(false);
            }

            if (Networking.IsOwner(gameObject))
            {
                RequestSerialization();
            }
        }

        public void SetScattering(bool scattering, bool reverseDirection = true)
        {
            if (reverseDirection && this.scattering != scattering)
            {
                if (ghostState == PacManGhostState.Normal || ghostState == PacManGhostState.Home || ghostState == PacManGhostState.Exiting
                    // This is afaik not normal PacMan behaviour, but is needed to accomidate slight timing differences
                    || ghostState == PacManGhostState.Entering && ghostManager.gameController.GameState == PacManGameState.AttractModeDemo 
                )
                {
                    turnAroundSoon = true;
                }
            }
            this.scattering = scattering;
            UpdateAnimator();
            RequestSerialization();
        }

        public void SetFrozen(bool frozen, bool ignoreIfCaught = false, bool keepAnimating = false)
        {
            if (frozen && !ignoreIfCaught)
            {
                frozenState = PacManGhostFrozenState.Frozen;
            }
            else if (frozen && ignoreIfCaught)
            {
                frozenState = PacManGhostFrozenState.FrozenIfNotCaught;
            }
            else
            {
                frozenState = PacManGhostFrozenState.NotFrozen;
            }
            animator.speed = frozen && !keepAnimating ? 0 : 1; // This would cause issues if the returning sprite was animated, luckily it isn't :)

            if (frozen == false && faceInStartingDirectionUntilUnfrozen)
            {
                faceInStartingDirectionUntilUnfrozen = false;
                UpdateAnimator();
            }
        }

        public void SetHousePelletCounterActive(bool active)
        {
            housePelletCounterActive = active;
        }

        public void SetHousePelletCounterLimit(int limit)
        {
            housePelletCounterLimit = limit;
        }

        public void SetWhite(bool white)
        {
            if (!isScared || !gameObject.activeInHierarchy)
                return;

            if (white)
            {
                animator.SetFloat("GhostType", 2);
            }
            else
            {
                animator.SetFloat("GhostType", 1);
            }
        }

        public void SetElroy(int elroyLevel)
        {
            // Debug.Log($"{gameObject} became Elroy level {elroyLevel}");
            if (elroyLevel > 0)
            {
                SetScattering(false, reverseDirection: false);
            }
            UpdateSpeed();
        }

        public void SetActive(bool active)
        {
            gameObject.SetActive(active);
            renderer.enabled = active;
            if (active)
            {
                UpdateAnimator();
            }
        }

        public void SetKinematic(bool kinematic)
        {
            this.kinematic = kinematic;
        }

        public void SetPredefinedPath(Vector2[] predefinedPath)
        {
            this.predefinedPath = predefinedPath;
            followingPredefinedPath = true;
            predefinedPathIndex = 0;
        }

        public void SetInTunnel(bool inTunnel)
        {
            this.inTunnel = inTunnel;
            UpdateSpeed();
        }

        public void SetSpecialLook(bool enabled)
        {
            specialLook = enabled;
            UpdateAnimator();
        }

        void SetVisibility(bool visible)
        {
            renderer.enabled = visible;
        }

        public PacManGhostState GetGhostState()
        {
            return ghostState;
        }

        public bool IsScared => isScared;

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

        public override void SetPosition(Vector2 position)
        {
            GridMoverTools.SetPosition(position, transform);
        }

        public override Vector2 GetDirection()
        {
            return direction;
        }

        public void SetDirection(Vector2 direction)
        {
            this.direction = direction;
            UpdateAnimator();
            RequestSerialization();
        }

        public void SetSpeed(float speed)
        {
            this.speed = speed;
            UpdateAnimator();
        }

        public override void OnPreSerialization()
        {
            syncedPosition = GetPosition();
        }

        public override void OnDeserialization()
        {
            SetPosition(syncedPosition);
            UpdateAnimator();
        }

        void OnTriggerEnter(Collider other)
        {
            if (Networking.IsOwner(gameObject) && other.gameObject.GetComponent<PacManGhostCollider>())
            {
                if (isScared)
                {
                    // Debug.Log($"{gameObject} was cought!");
                    ghostManager.GhostCaughtQueue(this);
                }
                else if (ghostState == PacManGhostState.Normal)
                {
                    // Debug.Log($"{gameObject} cought PacMan!");
                    ghostManager.CapturedPacMan();
                }
            }
            else if (other.gameObject.GetComponent<GhostHorizontalOnlyIndicator>())
            {
                horizontalOnly = true;
            }
            else if (other.gameObject.GetComponent<GhostTunnelIndicator>())
            {
                SetInTunnel(true);
            }
        }

        void OnTriggerExit(Collider other)
        {
            if (other.gameObject.GetComponent<GhostHorizontalOnlyIndicator>())
            {
                horizontalOnly = false;
            }
            else if (other.gameObject.GetComponent<GhostTunnelIndicator>())
            {
                SetInTunnel(false);
            }
        }

        PacManGhostState State
        {
            set
            {
                SetState(value);
            }
            get => ghostState;
        }
    }
}