#define RECORDING_DEMO

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

    public partial class GameManager : UdonSharpBehaviour
    {
        [Header("Static game components")]
        [SerializeField] private Maze[] mazes;
        [SerializeField] private PacMan pacMan;
        [SerializeField] private GhostManager ghostManager;
        [SerializeField] private BonusFruit bonusFruit;
        [SerializeField] private PelletManager pelletManager;
        [SerializeField] public StatusDisplay statusDisplay; // This one is public so other scripts can write to the debug display
        [SerializeField] private PelletManager attractScreen;
        [SerializeField] private GameObject intermissionScreen;
        [SerializeField] private GameObject pressStartButtonScreen;
        [SerializeField] private PlayerInput playerInput;
        [SerializeField] private Animator demo;
        [SerializeField] private SoundManager soundManager;

        [SerializeField] private GameObject recorder;


        [Header("Game settings")]
        [SerializeField] private int startingExtraLives = 3;
        [SerializeField] private int scoreToExtraLife = 10000;
        [Tooltip("Override amount of pellets needed to clear stage, set to -1 to disable.")]
        [SerializeField] private int pelletCountOverride = -1;

        private Maze maze;
        private VRCObjectPool pelletPool;
        private Intermission2Pole intermission2Pole;

        private Animator mazeSpriteAnimator;
        private int pelletCountTotal;
        private int pelletCountRemaining;
        private GameObject[] attractScreenElements;
        private GameObject[] intermissionScreenElements;

        [UdonSynced, FieldChangeCallback(nameof(GameState))] private PacManGameState gameState;
        [UdonSynced, FieldChangeCallback(nameof(Score))] private int score;
        [UdonSynced, FieldChangeCallback(nameof(Level))] private int level;
        [UdonSynced, FieldChangeCallback(nameof(HighScore))] private int highScore;
        [UdonSynced, FieldChangeCallback(nameof(ExtraLives))] private int extraLives;

        public void Start()
        {
            attractScreenElements = new GameObject[attractScreen.transform.childCount];
            for (int i = 0; i < attractScreenElements.Length; i++)
            {
                attractScreenElements[i] = attractScreen.transform.GetChild(i).gameObject;
            }

            intermissionScreenElements = new GameObject[intermissionScreen.transform.childCount];
            for (int i = 0; i < intermissionScreenElements.Length; i++)
            {
                intermissionScreenElements[i] = intermissionScreen.transform.GetChild(i).gameObject;
            }

            maze = mazes[0];
            pelletPool = maze.pelletContainer.GetComponent<VRCObjectPool>();
            mazeSpriteAnimator = maze.mazeSprite.GetComponent<Animator>();
            intermission2Pole = intermissionScreenElements[4].GetComponent<Intermission2Pole>();

            ghostManager.Initialize(maze.ghostTargets, pacMan, this);
            pacMan.Initialize(playerInput, pelletPool, this);
            bonusFruit.Initialize();
            pelletManager.Initialize(pelletPool);
            statusDisplay.Initialize();
            playerInput.Initialize(this);
            soundManager.Initialize();
            intermission2Pole.Initialize(this, ghostManager.Ghosts[0]);

            HideEverything();

            SetScore(0);
            SetHighScore(0);
            SetLevel(0);

            StartAttractMode();
        }

        public void FixedUpdate()
        {
            TimeSequenceUpdate(Time.deltaTime);
        }

        public void JoystickGrabbed()
        {
            if (gameState == PacManGameState.AttractMode || gameState == PacManGameState.AttractModeDemo)
                StartTimeSequence(PacManTimeSequence.WaitForStart);
        }

        public void JoystickReleased()
        {
            if (gameState == PacManGameState.WaitForStart)
                StartTimeSequence(PacManTimeSequence.WaitForStartTimeout);
        }

        public void StartGameButtonPressed()
        {
            Debug.Log($"{gameObject} Start Game Button was pressed!");
            TakeOwnership();
            StartTimeSequence(PacManTimeSequence.StartNewGame);
        }

        public void SkipLevelButtonPressed()
        {
            if (Networking.IsOwner(gameObject))
            {
                Debug.Log($"{gameObject} Skip level button pressed!");
                StartTimeSequence(PacManTimeSequence.BoardClear);
                TimeSequenceSkipToNextStep();
            }
        }

        public void StartDemoButtonPressed()
        {
            if (Networking.IsOwner(gameObject))
            {
                Debug.Log($"{gameObject} Start demo button pressed!");
                StartTimeSequence(PacManTimeSequence.Intermission1);
            }
        }

        private void StartAttractMode()
        {
// #if RECORDING_DEMO
            // recorder.gameObject.SetActive(true);
            StartTimeSequence(PacManTimeSequence.AttractScreenIntroduction);
// #else
//             SetGameState(PacManGameState.AttractMode);
//             HideEverything();
//             demo.gameObject.SetActive(true);
// #endif
        }

        private void InitializeNewGame()
        {
            Debug.Log($"{gameObject} Started new game!");
            SetScore(0);
            SetExtraLives(startingExtraLives);
            SetLevel(1);
        }

        private void InitializeLevel()
        {
            Debug.Log($"{gameObject} New level started!");

            if (Networking.IsOwner(gameObject))
            {
                pelletCountTotal = pelletPool.Pool.Length;
                pelletCountRemaining = pelletCountTotal;
                ghostManager.SetPelletsRemaining(pelletCountRemaining);
                ghostManager.NewLevel();

                pelletManager.RestoreAllPellets();

                if (pelletCountOverride > 0)
                {
                    pelletCountRemaining = pelletCountOverride;
                }
            }
            mazeSpriteAnimator.SetBool("Blinking", false);
        }

        private void RestartLevel()
        {
            Debug.Log($"{gameObject} (Re)started level!");

            // SetInGameComponentVisibility(true);

            ghostManager.Reset();
            pacMan.Reset();
            bonusFruit.Despawn();
            soundManager.Reset();
            pelletManager.SetPowerPelletsBlink(false);
        }

        public void GotPellet(bool addScore = true)
        {
            pelletCountRemaining--;
            if (addScore) AddScore(10);

            ghostManager.PelletConsumed();

            soundManager.PlayPelletSound();

            soundManager.UpdatePelletCount(pelletCountRemaining);

            int pelletsConsumed = pelletCountTotal - pelletCountRemaining;
            if (pelletCountRemaining <= 0)
            {
                StartTimeSequence(PacManTimeSequence.BoardClear);
            }
            else if (pelletsConsumed == 70 || pelletsConsumed == 170)
            {
                bonusFruit.Spawn();
            }
        }

        public void GotPowerPellet()
        {
            if (gameState == PacManGameState.AttractMode)
            {
                TimeSequenceSkipToNextStep();
                return;
            }
            GotPellet(addScore: false);
            AddScore(50);
            ghostManager.SetPowerPellet(true);
            pacMan.SetPowerPellet(true);
            soundManager.SetGhostBlue(true);
        }

        public void EndPowerPellet()
        {
            ghostManager.SetPowerPellet(false);
            pacMan.SetPowerPellet(false);
            soundManager.SetGhostBlue(false);
        }

        public void GotFruit()
        {
            AddScore(bonusFruit.Collected());
            soundManager.PlayFruitSound();
        }

        public void GhostCaught(int scoreBonus)
        {
            if (gameState == PacManGameState.AttractMode)
            {
                TimeSequenceSkipToNextStep();
                return;
            }

            AddScore(scoreBonus);
            StartTimeSequence(PacManTimeSequence.GhostCaught);
            pacMan.HideUntilUnfrozen();
        }

        public void PacManCaught()
        {
            StartTimeSequence(PacManTimeSequence.PacManCaught);
        }

        public void NoGhostsScared()
        {
            soundManager.SetGhostBlue(false);
        }

        public void NoGhostsRetreating()
        {
            soundManager.SetGhostRetreat(false);
        }

        public void Intermission2PoleUpdate()
        {
            TimeSequenceSkipToNextStep();
        }

        void BoardClearAnimation()
        {
            ghostManager.gameObject.SetActive(false);
            mazeSpriteAnimator.SetBool("Blinking", true);
        }

        
        private void HideEverything()
        {
            SetMazeActive(false);
            SetPelletsActive(false);
            SetMazeVisible(false);
            SetGhostsActive(false);
            SetPacManActive(false);
            SetPressStartButtonScreenVisible(false);
            SetIntermissionScreenVisible(false);
            statusDisplay.SetGameOverTextVisible(false);
            statusDisplay.SetExtraLivesDisplayVisible(false);
            statusDisplay.SetLevelDisplayVisible(false);
            statusDisplay.SetPlayer1TextVisible(false);
            statusDisplay.SetReadyTextVisible(false);
            demo.gameObject.SetActive(false);
        }

        void SetMazeActive(bool active)
        {
            maze.gameObject.SetActive(active);
        }

        void SetPelletsActive(bool active)
        {
            pelletPool.gameObject.SetActive(active);
        }

        void SetMazeVisible(bool visible)
        {
            mazeSpriteAnimator.SetBool("Hidden", !visible);
        }

        void SetGhostsActive(bool active)
        {
            ghostManager.SetActive(active);
        }

        void SetPacManActive(bool active)
        {
            pacMan.SetActive(active);
        }

        void SetPressStartButtonScreenVisible(bool visible)
        {
            pressStartButtonScreen.SetActive(visible);
        }

        void SetIntermissionScreenVisible(bool visible)
        {
            intermissionScreen.SetActive(visible);
        }

        void SetGameState(PacManGameState newGameState)
        {
            // Debug.Log($"{gameObject} State transitioning from {gameState} to {newGameState}");
            gameState = newGameState;
            if (Networking.IsOwner(gameObject))
            {
                RequestSerialization();
            }
        }

        private void IncrementLevel()
        {
            SetLevel(level + 1);
        }

        private void SetLevel(int level)
        {
            this.level = level;
            pacMan.SetLevel(level);
            ghostManager.SetLevel(level);
            statusDisplay.SetLevel(level);
            bonusFruit.SetFruitType(PacManConstants.GetFruitTypeForLevel(level));
        }

        void AddScore(int score)
        {
            if (gameState == PacManGameState.AttractMode || gameState == PacManGameState.AttractModeDemo)
            {
                return;
            }

            if (this.score < scoreToExtraLife && this.score + score >= scoreToExtraLife)
            {
                BonusLifeReached();
            }

            SetScore(this.score + score);
            RequestSerialization();
        }

        void SetScore(int score)
        {
            this.score = score;
            statusDisplay.Set1UPScore(score);
            if (score > highScore)
            {
                highScore = score;
                statusDisplay.SetHighScore(score);
            }
        }

        void SetHighScore(int highScore)
        {
            this.highScore = highScore;
            statusDisplay.SetHighScore(score);
        }

        public void DecrementLives()
        {
            if (!Networking.IsOwner(gameObject))
            {
                return;
            }
            // Debug.Log($"{gameObject} Decremented lives from {extraLives} to {extraLives - 1}");
            SetExtraLives(extraLives - 1);
        }

        void IncrementLives()
        {
            if (!Networking.IsOwner(gameObject))
            {
                return;
            }
            // Debug.Log($"{gameObject} Incremented lives from {extraLives} to {extraLives + 1}");
            SetExtraLives(extraLives + 1);
        }

        void SetExtraLives(int extraLives)
        {
            // Debug.Log($"{gameObject} Set lives from {this.extraLives} to {extraLives}");
            this.extraLives = extraLives;
            statusDisplay.SetExtraLives(extraLives);
        }

        void BonusLifeReached()
        {
            IncrementLives();
            soundManager.PlayExtraLifeSound();
        }

        public void SetFrozen(bool frozen, bool ghostIgnoreIfCaught = false, bool ghostKeepAnimating = false)
        {
            // Debug.Log($"{gameObject} Set Frozen: {frozen}");
            pacMan.SetFrozen(frozen);
            bonusFruit.SetFrozen(frozen);
            ghostManager.SetFrozen(frozen, ignoreIfCaught: ghostIgnoreIfCaught);

            if (!frozen)
            {
                pelletManager.SetPowerPelletsBlink(true);
            }
        }

        void TakeOwnership()
        {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);
            Networking.SetOwner(Networking.LocalPlayer, pacMan.gameObject);
            Networking.SetOwner(Networking.LocalPlayer, pelletPool.gameObject);
            ghostManager.SetOwner(Networking.LocalPlayer);
        }

        public int ExtraLives
        {
            set
            {
                SetExtraLives(value);
            }
            get => extraLives;
        }

        public PacManGameState GameState
        {
            set
            {
                SetGameState(value);
            }
            get => gameState;
        }

        public bool GhostsScared
        {
            set
            {

            }
            get => GhostsScared;
        }

        public int Score
        {
            set
            {
                SetScore(value);
            }
            get => score;
        }

        public int HighScore
        {
            set
            {
                SetHighScore(value);
            }
            get => score;
        }

        public int Level
        {
            set
            {
                SetLevel(value);
            }
            get => level;
        }

#region TIME SEQUENCE BEHAVIOUR
        // A note about the quality of the code here:
        // I intended to write this using proper classes, right until I realized Udon does not support instantiating classes.
        // While I'm not a big fan of the partial class solution that I ended up doing (static classes would still be neater, or perhaps separate UdonSharpBehaviour instances), 
        // I'm not redoing this unless I get instantiatable classes before I wrap up this project.

        bool currentlyInTimeSequence;
        bool waitingForTimeSequenceFinish;
        PacManTimeSequence currentTimeSequence;
        bool hasTimeSequenceQueued;
        private DataList timeSequenceQueue;
        [UdonSynced] float timeSequenceSecondsPassed;
        int timeSequenceProgress;
        float[] timeSequenceKeyframeTimes;

        private void StartTimeSequence(PacManTimeSequence timeSequence)
        {
            if (timeSequenceQueue == null)
            {
                timeSequenceQueue = new DataList();
            }

            if (currentlyInTimeSequence == true)
            {
                int timeSequenceInt = (int)timeSequence; // Doing the conversion in the line below crashes the script. I love working in Udon
                timeSequenceQueue.Add(timeSequenceInt);
                hasTimeSequenceQueued = true;

                return;
            }

            Debug.Log($"StartTimeSequence: {timeSequence}");

            currentlyInTimeSequence = true;
            currentTimeSequence = timeSequence;
            timeSequenceProgress = 0;
            timeSequenceSecondsPassed = 0;
            timeSequenceKeyframeTimes = GetTimeSequenceKeyframeTimes(timeSequence);
            TimeSequenceProgressToTime(timeSequenceSecondsPassed);
        }

        private void InsertTimeSequence(PacManTimeSequence timeSequence)
        {
            StartTimeSequence(timeSequence);
        }

        private void TimeSequenceUpdate(float deltaSeconds)
        {
            if (!currentlyInTimeSequence && hasTimeSequenceQueued)
            {
                timeSequenceQueue.TryGetValue(0, out DataToken nextTimeSequence);
                StartTimeSequence((PacManTimeSequence)nextTimeSequence.Int);
                timeSequenceQueue.RemoveAt(0);
                hasTimeSequenceQueued = timeSequenceQueue.Count > 0 ? true : false;
                return;
            }

            if (currentlyInTimeSequence)
            {
                if (hasTimeSequenceQueued)
                {
                    while (currentlyInTimeSequence)
                    {
                        TimeSequenceSkipToNextStep();
                    }
                }
                else
                {
                    TimeSequenceProgressToTime(timeSequenceSecondsPassed + deltaSeconds);
                }
            }
        }

        private void TimeSequenceSkipToNextStep()
        {
            // Debug.Log($"{gameObject} TimeSequenceSkipToNextStep");
            if (timeSequenceProgress < timeSequenceKeyframeTimes.Length)
            {
                TimeSequenceProgressToTime(timeSequenceKeyframeTimes[timeSequenceProgress]);
            }
            else
            {
                Debug.LogWarning($"{gameObject} Tried skipping to next time sequence step when already on last step!");
                currentlyInTimeSequence = false;
            }
        }

        private void TimeSequenceProgressToTime(float seconds)
        {
            timeSequenceSecondsPassed = seconds;
            while (timeSequenceSecondsPassed >= timeSequenceKeyframeTimes[timeSequenceProgress])
            {
                TimeSequenceExecuteStep(timeSequenceProgress);

                timeSequenceProgress += 1;
                if (timeSequenceProgress >= timeSequenceKeyframeTimes.Length)
                {
                    currentlyInTimeSequence = false;
                    TimeSequencePrepareForFinish();
                    break;
                }
            }
        }

        private void TimeSequencePrepareForFinish()
        {
            if (Networking.IsOwner(gameObject))
            {
                TimeSequenceExecuteFinalize();
                if (!hasTimeSequenceQueued)
                {
                    TimeSequenceExecuteFinished();
                }
            }
            else
            {
                waitingForTimeSequenceFinish = true;
            }
        }

        private void TimeSequenceExecuteStep(int sequenceProgress)
        {
            // Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}");
            switch (currentTimeSequence)
            {
                default:
                    Debug.LogError($"{gameObject} No time sequence keyframes known for sequence {currentTimeSequence}");
                    break;
                case PacManTimeSequence.AttractScreenIntroduction:
                    TimeSequenceStepAttractScreenIntroduction(sequenceProgress);
                    break;
                case PacManTimeSequence.AttractScreenDemo:
                    TimeSequenceStepAttractScreenDemo(sequenceProgress);
                    break;
                case PacManTimeSequence.WaitForStart:
                    TimeSequenceStepWaitForStart(sequenceProgress);
                    break;
                case PacManTimeSequence.StartNewGame:
                    TimeSequenceStepStartNewGame(sequenceProgress);
                    break;
                case PacManTimeSequence.BoardClear:
                    TimeSequenceStepBoardClear(sequenceProgress);
                    break;
                case PacManTimeSequence.StartNewLevel:
                    TimeSequenceStepStartNewLevel(sequenceProgress);
                    break;
                case PacManTimeSequence.GhostCaught:
                    TimeSequenceStepGhostCaught(sequenceProgress);
                    break;
                case PacManTimeSequence.PacManCaught:
                    TimeSequenceStepPacManCaught(sequenceProgress);
                    break;
                case PacManTimeSequence.RestartLevel:
                    TimeSequenceStepRestartLevel(sequenceProgress);
                    break;
                case PacManTimeSequence.GameOver:
                    TimeSequenceStepGameOver(sequenceProgress);
                    break;
                case PacManTimeSequence.Intermission1:
                    TimeSequenceStepIntermission1(sequenceProgress);
                    break;
                case PacManTimeSequence.Intermission2:
                    TimeSequenceStepIntermission2(sequenceProgress);
                    break;
                case PacManTimeSequence.Intermission3:
                    TimeSequenceStepIntermission3(sequenceProgress);
                    break;
                case PacManTimeSequence.AttractScreenWaitToRestart:
                case PacManTimeSequence.WaitForStartTimeout:
                    // These don't have steps
                    break;
            }
        }

        private void TimeSequenceExecuteFinalize()
        {
            // Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}");
            switch (currentTimeSequence)
            {
                default:
                    Debug.LogError($"{gameObject} No time sequence finalize known for sequence {currentTimeSequence}");
                    break;
                case PacManTimeSequence.AttractScreenIntroduction:
                    TimeSequenceFinalizeAttractScreenIntroduction();
                    break;
                case PacManTimeSequence.WaitForStart:
                    TimeSequenceFinalizeWaitForStart();
                    break;
                case PacManTimeSequence.WaitForStartTimeout:
                    TimeSequenceFinalizeWaitForStart();
                    break;
                case PacManTimeSequence.StartNewGame:
                    TimeSequenceFinalizeStartNewGame();
                    break;
                case PacManTimeSequence.StartNewLevel:
                    TimeSequenceFinalizeStartNewLevel();
                    break;
                case PacManTimeSequence.GhostCaught:
                    TimeSequenceFinalizeGhostCaught();
                    break;
                case PacManTimeSequence.RestartLevel:
                    TimeSequenceFinalizeRestartLevel();
                    break;
                case PacManTimeSequence.Intermission2:
                    TimeSequenceFinalizeIntermission2();
                    break;
                case PacManTimeSequence.Intermission3:
                    TimeSequenceFinalizeIntermission3();
                    break;
                case PacManTimeSequence.AttractScreenDemo:
                case PacManTimeSequence.AttractScreenWaitToRestart:
                case PacManTimeSequence.GameOver:
                case PacManTimeSequence.Intermission1:
                case PacManTimeSequence.PacManCaught:
                case PacManTimeSequence.BoardClear:
                    // These don't have a finalize
                    break;
            }
        }

        private void TimeSequenceExecuteFinished()
        {
            // Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}");
            switch (currentTimeSequence)
            {
                default:
                    Debug.LogError($"{gameObject} No time sequence finish known for sequence {currentTimeSequence}");
                    break;
                case PacManTimeSequence.AttractScreenIntroduction:
                    TimeSequenceFinishedAttractScreenIntroduction();
                    break;
                case PacManTimeSequence.AttractScreenDemo:
                    TimeSequenceFinishedAttractScreenDemo();
                    break;
                case PacManTimeSequence.AttractScreenWaitToRestart:
                    TimeSequenceFinishedAttractScreenWaitToRestart();
                    break;
                case PacManTimeSequence.BoardClear:
                    TimeSequenceFinishedBoardClear();
                    break;
                case PacManTimeSequence.PacManCaught:
                    TimeSequenceFinishedPacManCaught();
                    break;
                case PacManTimeSequence.GameOver:
                    TimeSequenceFinishedGameOver();
                    break;
                case PacManTimeSequence.Intermission1:
                    TimeSequenceFinishedIntermission1();
                    break;
                case PacManTimeSequence.Intermission2:
                    TimeSequenceFinishedIntermission2();
                    break;
                case PacManTimeSequence.Intermission3:
                    TimeSequenceFinishedIntermission3();
                    break;
                case PacManTimeSequence.RestartLevel:
                case PacManTimeSequence.StartNewLevel:
                case PacManTimeSequence.GhostCaught:
                case PacManTimeSequence.WaitForStart:
                case PacManTimeSequence.WaitForStartTimeout:
                case PacManTimeSequence.StartNewGame:
                    // These don't have a finished
                    break;
            }
        }

        private float[] GetTimeSequenceKeyframeTimes(PacManTimeSequence timeSequence)
        {
            switch (timeSequence)
            {
                default:
                    Debug.LogError($"{gameObject} No time sequence keyframe times known for sequence {timeSequence}");
                    return new float[0];
                case PacManTimeSequence.AttractScreenIntroduction:
                    return DeltaToAbsolute(new float[] { 0, 0.032f, 1f, 1f, .5f, .5f, 1f, .5f, .5f, 1f, .5f, .5f, 1f, .5f, 1f, 1f, 1f,
                                                         5f, 0.2f, 2f, 0.91667f, 2f, 0.91667f, 2f, 0.91667f, 2f, 0.91667f });
                case PacManTimeSequence.AttractScreenDemo:
                    return DeltaToAbsolute(new float[] { 0, 0.016f, 0.05f, 0.16f, 0.33f, 1.85f, 54f });
                case PacManTimeSequence.AttractScreenWaitToRestart:
                    return DeltaToAbsolute(new float[] { 0, 2f });
                case PacManTimeSequence.WaitForStart:
                    return DeltaToAbsolute(new float[] { 0, 0.016f });
                case PacManTimeSequence.StartNewGame:
                    return DeltaToAbsolute(new float[] { 0, 0.016f, 2.2f, 0.032f, 0.032f, 1.92f, 0.032f });
                case PacManTimeSequence.BoardClear:
                    return DeltaToAbsolute(new float[] { 0, 2f, 0.016f, 1.6f - 0.016f, 0.016f, 0.032f, 0.3f });
                case PacManTimeSequence.StartNewLevel:
                    return DeltaToAbsolute(new float[] { 0, 0.064f, 0.032f, 1.85f, 0.016f });
                case PacManTimeSequence.GhostCaught:
                    return DeltaToAbsolute(new float[] { 0, 0.91667f });
                case PacManTimeSequence.PacManCaught:
                    return DeltaToAbsolute(new float[] { 0, 1, 0.35f, 2.40f });
                case PacManTimeSequence.RestartLevel:
                    return DeltaToAbsolute(new float[] { 0, 0.016f, 0.064f, 0.032f, 1.85f, 0.016f });
                case PacManTimeSequence.GameOver:
                    return DeltaToAbsolute(new float[] { 0, 1.95f });
                case PacManTimeSequence.Intermission1:
                    return DeltaToAbsolute(new float[] { 0, 0.316f, 0.3f, 3.96f, 2.25f, 3.93f });
                case PacManTimeSequence.Intermission2:
                    return DeltaToAbsolute(new float[] { 0, 0.25f, 0.083f, 0.3f, 1.43f, 2.5f, 1.816f, 1.25f, 0.017f, 1f, 1.966f, 0.033f });
                case PacManTimeSequence.Intermission3:
                    return DeltaToAbsolute(new float[] { 0, 0.316f, 0.7f, 3.35f, 0.83f, 3.67f });
            }
        }

        private static float[] DeltaToAbsolute(float[] delta)
        {
            if (delta.Length < 1)
            {
                return new float[0];
            }

            float[] absolute = new float[delta.Length];
            absolute[0] = delta[0];
            for (int i = 1; i < delta.Length; i++)
            {
                absolute[i] = delta[i] + absolute[i - 1];
            }
            return absolute;
        }

        public int TimeSequenceProgress
        {
            get => timeSequenceProgress;
        }

        public float TimeSequenceSecondsPassed
        {
            get => timeSequenceSecondsPassed;
            set => TimeSequenceProgressToTime(value);
        }
#endregion
    }
}