using System.Collections.Generic;
using Nox.Platform;
using Unity.Profiling;
using UnityEngine;

namespace Nox.Core {
    /// <summary>
    /// This is the class that will execute the update cycles for each game state. This should be the only place using Unity's Monobehaviour specific classes
    /// </summary>
    public class GameStateRunner : MonoBehaviour {
        private IGameState currentState;
        private GameState currentStateType;
        private Dictionary<GameState, IGameState> applicationStates;
        private GameDataState gameDataState;
        private bool isRunnerUpdated;
        private bool isUnloading;
        private IPlatform platform;
        private ISceneTransition sceneTransition;
        private GameState pendingStateTransition;
        private bool isStateTransitioning;
        private bool waitingForFadeIn;

        public void Initialize(Dictionary<GameState, IGameState> stateLookUp, GameDataState gameDataState, IPlatform selectedPlatform, ISceneTransition sceneTransition) {
            DontDestroyOnLoad(gameObject);
            this.platform = selectedPlatform;
            this.sceneTransition = sceneTransition;
            applicationStates = stateLookUp;
            this.gameDataState = gameDataState;
            ChangeApplicationState(gameDataState.ActiveGameState);
        }

        public void FixedUpdate() {
            if(!currentState.IsGameStateInitialized) {
                return;
            }
        }

        public void Update() {
            // Pre-ticks area
            platform.Tick();

            if(waitingForFadeIn) {
                if(currentState.IsGameStateInitialized) {
                    waitingForFadeIn = false;
                    sceneTransition.FadeIn(() => {
                        isStateTransitioning = false;
                    });
                }
                return;
            }

            if(isStateTransitioning) {
                return;
            }

            if(!isRunnerUpdated && currentState.IsGameStateInitialized) {
                isRunnerUpdated = true;
            }

            var nextState = currentStateType;
            if(isRunnerUpdated || isUnloading) {
                nextState = currentState.Tick();
                if(gameDataState.ActiveGameState != currentStateType) {
                    nextState = gameDataState.ActiveGameState;
                }
            }

            if(nextState != currentStateType) {
                BeginStateTransition(nextState);
            }

            // Ticks area

            // Post-ticks area
        }

        public void LateUpdate() {
            if(isStateTransitioning || !currentState.IsGameStateInitialized) {
                return;
            }
            currentState.LateTick();
        }

        public void OnDestroy() {
            currentState.Dispose();
        }

        private void BeginStateTransition(GameState newGameState) {
            isStateTransitioning = true;
            pendingStateTransition = newGameState;
            sceneTransition.FadeOut(() => {
                ChangeApplicationState(pendingStateTransition);
                waitingForFadeIn = true;
            });
        }

        private void ChangeApplicationState(GameState newGameState) {
            if(currentState != null) {
                currentState.ExitGameState();
            }

            currentState = applicationStates[newGameState];
            currentState.EnterGameState();
            currentStateType = newGameState;
            gameDataState.ChangeGameState(newGameState);
            isRunnerUpdated = false;
            Debug.Log($"Entered {newGameState} ApplicationState");
        }
    }
}
