using System.Collections.Generic;
using System.Threading.Tasks;
using Nox.Game;
using Nox.Game.UI;
using Nox.Platform;
using Jovian.SaveSystem;
using Jovian.InGameLogging;
using UnityEngine;
using UnityEngine.AddressableAssets;

namespace Nox.Core {
    public class EntryPoint : MonoBehaviour {
        private Dictionary<GameState, IGameState> applicationStates;
        private ISceneTransition sceneTransition;
        private ISaveSystem saveSystem;
        private IGameLogStore gameLogStore;
        
        private BootstrapReferences bootstrapReferences;
        private MenuGameStateData menuGameStateData;

        public async Task StartGame(GameDataState gameDataState, InitializerSettingsFile initializerSettings, IPlatform selectedPlatform) {
            DontDestroyOnLoad(this);
            // scene authoring is helping with making each scene stand-alone playable
            var activeSceneReference = FindFirstObjectByType<SceneReference>();
            if(!activeSceneReference) {
                Debug.LogWarning("The scene has no SceneReference script. Will start by default in Splash Application State");
                var sceneRef = new GameObject {
                    name = "Scene Reference"
                };
                activeSceneReference = sceneRef.AddComponent<SceneReference>();
                activeSceneReference.gameState = GameState.BootState;
                await Task.Yield();
            }
            
            gameDataState.ChangeGameState(activeSceneReference.gameState);

            var bootStrapsSettingsHandle = Addressables.LoadAssetAsync<BootstrapReferences>(initializerSettings.bootstrapSettings);
            await bootStrapsSettingsHandle.Task;
            bootstrapReferences = bootStrapsSettingsHandle.Result;
            menuGameStateData = new MenuGameStateData();

            var fadeObject = new GameObject("ScreenFadeTransition");
            sceneTransition = fadeObject.AddComponent<ScreenFadeTransition>();

            CreateApplicationStates(gameDataState, selectedPlatform);

            var gameStateRunner = gameObject.AddComponent<GameStateRunner>();
            gameStateRunner.Initialize(applicationStates, gameDataState, selectedPlatform, sceneTransition);
        }

        private void CreateApplicationStates(GameDataState gameDataState, IPlatform selectedPlatform) {
            // Save System
            var saveSettings = SaveSystemSettings.Load();
            ISaveSerializer saveSerializer = saveSettings.saveFormat == SaveFormat.Json
                ? new JsonSaveSerializer()
                : new BinarySaveSerializer(saveSettings.obfuscationKey);
            ISaveStorage saveStorage = new FileSystemSaveStorage(Application.persistentDataPath, saveSettings.saveDirectoryName);
            ISaveSlotManager saveSlotManager = new SaveSlotManager(saveStorage, saveSettings);
            saveSystem = new SaveSystem(saveSerializer, saveStorage, saveSlotManager, saveSettings);

            gameLogStore = new GameLogStore(500);

            var adventureData = new AdventureData();
            
            applicationStates = new Dictionary<GameState, IGameState>();
            applicationStates.TryAdd(GameState.BootState, 
                new SplashGameState(bootstrapReferences, gameDataState));
            applicationStates.TryAdd(GameState.MainMenu, 
                new MainMenuGameState(gameDataState, menuGameStateData, bootstrapReferences, saveSystem, adventureData, gameLogStore));
            applicationStates.TryAdd(GameState.GameMode, 
                new GameModeGameState(gameDataState, bootstrapReferences, selectedPlatform.PlatformSettings, saveSystem, sceneTransition, adventureData, gameLogStore));
        }
    }
}
