using UdonSharp;
using UnityEngine;

namespace Marro.PacManUdon
{
    public partial class GameManager : SyncedObject
    {
        [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] private StatusDisplay statusDisplay;
        [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 NetworkManager networkManagerSetup;

        [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 Intermission2Pole intermission2Pole;

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

        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];
            mazeSpriteAnimator = maze.mazeSprite.GetComponent<Animator>();
            intermission2Pole = intermissionScreenElements[4].GetComponent<Intermission2Pole>();

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

            HideEverything();

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

            StartAttractMode();
        }

        public override void SyncedUpdate()
        {
            TimeSequenceUpdate(networkManager.SyncedDeltaTime);
        }

        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 ResetButtonPressed()
        {
            Debug.Log($"{gameObject} Reset button was pressed!");
            Start();
        }

        public void StartGameButtonPressed()
        {
            Debug.Log($"{gameObject} Start Game Button was pressed!");
            if (networkManager.IsOwner)
            {
                networkManager.SendEventNow(NetworkEventType.StartGameButtonPressed);
            }
            StartTimeSequence(PacManTimeSequence.StartNewGame);
        }

        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!");

            pelletCountTotal = pelletManager.PelletCount;
            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);
        }

        private void PrepareForCutscene()
        {
            HideEverything();
            RestartLevel();
            SetFrozen(true);
        }

        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()
        {
            Debug.Log($"{gameObject} 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)
        {
            Debug.Log($"{gameObject} GhostCaught");

            if (gameState == PacManGameState.AttractMode)
            {
                TimeSequenceSkipToNextStep();
                return;
            }

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

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

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

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

        public void Intermission2PoleUpdate()
        {
            Debug.Log($"{gameObject} Intermission2PoleUpdate");

            TimeSequenceSkipToNextStep();
        }

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


        private void HideEverything()
        {
            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 SetPelletsActive(bool active)
        {
            pelletManager.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;
        }

        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);
        }

        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()
        {
            // Debug.Log($"{gameObject} Decremented lives from {extraLives} to {extraLives - 1}");
            SetExtraLives(extraLives - 1);
        }

        void IncrementLives()
        {
            // 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);
            }
        }

        public override void CollectSyncedData(byte[] data, ref int offset, NetworkEventType eventType)
        {
            //data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)gameState) };
            //data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence);
            //data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)currentTimeSequence) };
            //data[offset++] = BitConverter.GetBytes(timeSequenceSecondsPassed);
        }

        public override bool WriteSyncedData(byte[] data, ref int offset, NetworkEventType eventType)
        {
            if (eventType == NetworkEventType.StartGameButtonPressed)
            {
                StartGameButtonPressed();
            }

            //SetGameState((PacManGameState)data[offset++]);

            //var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++);
            //var currentTimeSequence = (PacManTimeSequence)data[offset++];
            //var timeSequenceSecondsPassed = BitConverter.ToSingle(data, offset);
            //offset += 4;
            //TimeSequenceSyncWithRemote(currentlyInTimeSequence, currentTimeSequence, timeSequenceSecondsPassed);

            return true;
        }

        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;
        }
    }
}