using System;
using System.Reflection;
using GamecraftModdingAPI.Blocks;
using HarmonyLib;

using RobocraftX;
using RobocraftX.Services;
using Svelto.Context;

using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Tasks;

namespace GamecraftModdingAPI
{
    /// <summary>
    /// The main class of the GamecraftModdingAPI.
    /// Use this to initialize the API before calling it.
    /// </summary>
    public static class Main
    {
        private static Harmony harmony;

        public static bool IsInitialized { 
            get { return harmony != null; }
        }

        private static int referenceCount = 0;

        /// <summary>
        /// Initializes the GamecraftModdingAPI. 
        /// Call this as soon as possible after Gamecraft starts up.
        /// Ideally, this should be called from your main Plugin class's OnApplicationStart() method.
        /// </summary>
        public static void Init()
        {
            referenceCount++;
            if (referenceCount > 1) { return; }
            if (IsInitialized)
            {
                Logging.LogWarning("GamecraftModdingAPI.Main.Init() called but API is already initialized!");
                return;
            }
            Logging.MetaDebugLog($"Patching Gamecraft");
            var currentAssembly = Assembly.GetExecutingAssembly();
            harmony = new Harmony(currentAssembly.GetName().Name);
            try
            {
                harmony.PatchAll(currentAssembly);
            }
            catch (Exception e)
            { //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
                Logging.Log(e.ToString());
                Logging.LogWarning("Failed to patch Gamecraft. Attempting to patch to display error...");
                harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized")
                        .MakeGenericMethod(typeof(UnityContext<FullGameCompositionRoot>)),
                    new HarmonyMethod(((Action) OnPatchError).Method)); //Can't use lambdas here :(
                return;
            }

            // init utility
            Logging.MetaDebugLog($"Initializing Utility");
#pragma warning disable 0612,0618
            Utility.GameState.Init();
			Utility.VersionTracking.Init();
            // create default event emitters
            Logging.MetaDebugLog($"Initializing Events");
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false));
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false));
            EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false));
			EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.buildEngine);
			EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.simEngine);
#pragma warning restore 0612,0618
            // init block implementors
            Logging.MetaDebugLog($"Initializing Blocks");
			// init inventory
			Inventory.Hotbar.Init();
			// init input
			Input.FakeInput.Init();
			// init object-oriented classes
			Player.Init();
            Block.Init();
            BlockGroup.Init();
            Wire.Init();
            GameClient.Init();
            AsyncUtils.Init();
			GamecraftModdingAPI.App.Client.Init();
			GamecraftModdingAPI.App.Game.Init();
            Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
        }

        /// <summary>
        /// Shuts down & cleans up the GamecraftModdingAPI.
        /// Call this as late as possible before Gamecraft quits.
        /// Ideally, this should be called from your main Plugin class's OnApplicationQuit() method.
        /// </summary>
        public static void Shutdown()
        {
            if (referenceCount > 0) { referenceCount--; }
            if (referenceCount == 0)
            {
                if (!IsInitialized)
                {
                    Logging.LogWarning("GamecraftModdingAPI.Main.Shutdown() called but API is not initialized!");
                    return;
                }
                Scheduler.Dispose();
                var currentAssembly = Assembly.GetExecutingAssembly();
                harmony.UnpatchAll(currentAssembly.GetName().Name);
                harmony = null;
                Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
            }
        }

        private static void OnPatchError()
        {
            ErrorBuilder.DisplayMustQuitError("Failed to patch Gamecraft!\n" +
                                              "Make sure you're using the latest version of GamecraftModdingAPI or disable mods if the API isn't released yet.");
        }
    }
}