using System;
using System.Linq;
using System.Reflection;

using HarmonyLib;
// test
using Svelto.ECS;
using RobocraftX.Blocks;
using RobocraftX.Common;
using RobocraftX.SimulationModeState;

using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Blocks;

namespace GamecraftModdingAPI.Tests
{
    // unused by design
    /// <summary>
    /// Modding API implemented as a standalone IPA Plugin.
    /// Ideally, GamecraftModdingAPI should be loaded by another mod; not itself
    /// </summary>
    public class GamecraftModdingAPIPluginTest
#if DEBUG
        : IllusionPlugin.IEnhancedPlugin
#endif
    {      

        private static Harmony harmony { get; set; }

        public string[] Filter { get; } = new string[] { "Gamecraft", "GamecraftPreview" };

        public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;

        public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();

        public string HarmonyID { get; } = "org.git.exmods.modtainers.gamecraftmoddingapi";

        public void OnApplicationQuit()
        {
            GamecraftModdingAPI.Main.Shutdown();
        }

        public void OnApplicationStart()
        {
			FileLog.Reset();
			Harmony.DEBUG = true;
            GamecraftModdingAPI.Main.Init();
			Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}");
			// in case Steam is not installed/running
			// this will crash the game slightly later during startup
			//SteamInitPatch.ForcePassSteamCheck = true;
			// in case running in a VM
			//MinimumSpecsCheckPatch.ForcePassMinimumSpecCheck = true;
			// disable some Gamecraft analytics
			//AnalyticsDisablerPatch.DisableAnalytics = true;
			// disable background music
			Logging.MetaDebugLog("Audio Mixers: "+string.Join(",", AudioTools.GetMixers()));
			//AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(

			Utility.VersionTracking.Enable();

            // debug/test handlers
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("App Inited event!"); }, () => { },
                EventType.ApplicationInitialized, "appinit API debug"));
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Activated event!"); },
                () => { Logging.Log("Menu Destroyed event!"); },
                EventType.Menu, "menuact API debug"));
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Switched To event!"); }, () => { },
                EventType.MenuSwitchedTo, "menuswitch API debug"));
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Activated event!"); },
                () => { Logging.Log("Game Destroyed event!"); },
                EventType.Game, "gameact API debug"));
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Reloaded event!"); }, () => { },
                EventType.GameReloaded, "gamerel API debug"));
            EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Switched To event!"); }, () => { },
                EventType.GameSwitchedTo, "gameswitch API debug"));
			EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Mode Simulation Switched To event!"); }, () => { },
			    EventType.SimulationSwitchedTo, "simulationswitch API debug"));
			EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Mode Build Switched To event!"); }, () => { },
                EventType.BuildSwitchedTo, "buildswitch API debug"));

			EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { throw new Exception(""); }, () => {},
			    EventType.Menu, "menu activated API error thrower test"));

            // debug/test commands
			if (Dependency.Hell("ExtraCommands"))
			{
				CommandBuilder.Builder()
                              .Name("Exit")
                              .Description("Close Gamecraft immediately, without any prompts")
    		                  .Action(() => { UnityEngine.Application.Quit(); })
    		                  .Build();
                
				CommandBuilder.Builder()
				              .Name("SetFOV")
				              .Description("Set the player camera's field of view")
				              .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
                              .Build();

				CommandBuilder.Builder()
					.Name("MoveLastBlock")
					.Description("Move the most-recently-placed block, and any connected blocks by the given offset")
					.Action((float x, float y, float z) =>
					{
						if (GameState.IsBuildMode())
							foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
								block.Position += new Unity.Mathematics.float3(x, y, z);
						else
							GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
					}).Build();

				CommandBuilder.Builder()
				              .Name("PlaceAluminium")
				              .Description("Place a block of aluminium at the given coordinates")
				              .Action((float x, float y, float z) => { Block.PlaceNew(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); })
                              .Build();

				System.Random random = new System.Random(); // for command below
				CommandBuilder.Builder()
				              .Name("RandomizeSignalsInputs")
				              .Description("Do the thing")
				              .Action(() => {
                                  if (!GameState.IsSimulationMode())
                                  {
                                      Logging.CommandLogError("You must be in simulation mode for this to work!");
                                      return;
                                  }
                                  Tasks.Repeatable task = new Tasks.Repeatable(
						            () => {
                                      uint count = 0;
                                      EGID[] eBlocks = Blocks.Signals.GetElectricBlocks();
                                      for (uint i = 0u; i < eBlocks.Length; i++)
                                      {
                                          uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]);
                                          for (uint j = 0u; j < ids.Length; j++)
                                          {
                                              Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble());
                                              count++;
                                          }
                                      }
                                      Logging.MetaDebugLog($"Did the thing on {count} inputs");
                                  },
						            () => { return GameState.IsSimulationMode(); });
                                  Tasks.Scheduler.Schedule(task);
                              }).Build();

				CommandBuilder.Builder("getBlock")
					.Action(() => uREPL.Log.Output(new Player(Players.PlayerType.Local).GetBlockLookedAt()+"")).Build();
				CommandBuilder.Builder("changeToAluminium")
					.Action(() => new Player(Players.PlayerType.Local).GetBlockLookedAt().Type = BlockIDs.AluminiumCube)
					.Build();

				CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
							  .Action(() => { throw new Exception("Error Command always throws an error"); })
							  .Build();

                /*
                CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
                    "SetFOV", "Set the player camera's field of view"));
                CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
                    (x, y, z) => {
                        bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks(
                            GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID,
                            new Unity.Mathematics.float3(x, y, z));
                        if (!success)
                        {
                            GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
                        }
                    }, "MoveLastBlock", "Move the most-recently-placed block, and any connected blocks by the given offset"));
                CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
                    (x, y, z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); },
                    "PlaceAluminium", "Place a block of aluminium at the given coordinates"));
				System.Random random = new System.Random(); // for command below
				CommandManager.AddCommand(new SimpleCustomCommandEngine(
					() => { 
					    if (!GameState.IsSimulationMode())
					    {
							Logging.CommandLogError("You must be in simulation mode for this to work!");
							return;
					    }
					    Tasks.Repeatable task = new Tasks.Repeatable(() => {
    						    uint count = 0;
						        EGID[] eBlocks = Blocks.Signals.GetElectricBlocks();
                                for (uint i = 0u; i < eBlocks.Length; i++)
                                {
							        uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]);
                                    for (uint j = 0u; j < ids.Length; j++)
                                    {
								        Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble());
                                        count++;
                                    }
                                }
                                Logging.MetaDebugLog($"Did the thing on {count} inputs");
					        },
					                                                 () => { return GameState.IsSimulationMode(); });
						Tasks.Scheduler.Schedule(task);
					}, "RandomizeSignalsInputs", "Do the thing"));
			*/
			}

            // dependency test
            if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0")))
			{
				Logging.LogWarning("You're in GamecraftScripting dependency hell");
			}
			else
			{
				Logging.Log("Compatible GamecraftScripting detected");
			}
        }

        public void OnFixedUpdate() { }

        public void OnLateUpdate() { }

        public void OnLevelWasInitialized(int level) { }

        public void OnLevelWasLoaded(int level) { }

        public void OnUpdate() { }
    }
}