using gdio.common.objects;
using gdio.unreal_api;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace StackOBotTests
{
    public partial class Tests
    {
        [DllImport("user32.dll")]
        public static extern int SetForegroundWindow(int hwnd);
        public static bool standalone = false;

        //These parameters can be used to override settings used to test when running from the NUnit command line
        public string testMode = TestContext.Parameters.Get("Mode", "IDE");
        public string editorVersion = TestContext.Parameters.Get("EditorVersion", "5.1"); //4.27 5.0 5.1
        public string testProjName = TestContext.Parameters.Get("ProjectName", "StackOBot"); //StackOBot MyProject
        public string pathToExe = TestContext.Parameters.Get("PathToExe", string.Empty);
        //public string pathToExe = @"D:\Work\UnrealProjects\5.1\StackOBot\Build-Win\WindowsNoEditor\StackOBot.exe";


        ApiClient api;


        [OneTimeSetUp]
        public void Setup()
        {
            if (standalone)
            {
                Process[] appsrunning = Process.GetProcessesByName(testProjName);
                if (appsrunning.Length == 0)
                {
                    Process unreal = System.Diagnostics.Process.Start(pathToExe);
                }
                else
                {
                    SetForegroundWindow((int)appsrunning[0].MainWindowHandle);
                }
            }
            else
            {
                Process[] appsrunning;
                if (editorVersion == "4.27")
                    appsrunning = Process.GetProcessesByName("UE4Editor");
                else
                    appsrunning = Process.GetProcessesByName("UnrealEditor");

                if (appsrunning.Length == 0)
                {
                    Console.WriteLine($"If you want to test in the editor, launch the editor and start the game first");
                    Assert.Fail();//process has to run, and the PIE editor running. 
                                  //  Process unreal = System.Diagnostics.Process.Start("E:\\UE_4.27\\Engine\\Binaries\\Win64\\UE4Editor.exe");
                }
                else
                {
                    SetForegroundWindow((int)appsrunning[0].MainWindowHandle);
                }
            }

            api = new ApiClient();
            api.Connect("localhost");
            api.Wait(1000);

            //Required when using Vector2InputEvent api call.
            api.CreateInputDevice("GDIO", "IMC_ThirdPersonControls");
        }

        [OneTimeTearDown]
        public void Disconnect()
        {
            api.RemoveInputDevices(); 
            api.Disconnect();
        }

        [Test, Order(0)]
        public void DoorPuzzle()
        {
            //Save the position of objects to check for proximity later
            Vector3 cratesPos = api.GetObjectPosition("/BP_Crate_C_UAID_B42E9936F54257D500_1757448073");
            Vector3 switch1Pos = api.GetObjectPosition("//BP_PressurePlate_C_UAID_B42E9936F5429ADA00_2086841185");
            Vector3 switch2Pos = api.GetObjectPosition("//BP_PressurePlate_C_UAID_B42E9936F5429ADA00_2086841184");

            Vector3 leftDoorPos = api.GetObjectPosition("//BP_Door_C_UAID_B42E9936F5429ADA00_2086828164/fn:component('DoorLeft')");

            LookAtCrates();

            //Move towards the crates
            
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keydown frames 0 - KeyDown hold until the next call.

            //Wait until the player reaches close to the crates
            while (!IsAt("//BP_Bot_C_0 ", cratesPos,new Vector2(350,150)));

            //Stop moving
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keyup

            LookAtSwitch1();

            //Move towards Switch 1
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keydown

            //Wait until the player reaches the switch
            while (!IsAt("//BP_Bot_C_0 ", switch1Pos, new Vector2(35, 65))) ;

            //Stop moving
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keyup


            //print a new bot (Press F)
            api.KeyPress(new KeyCode[] { KeyCode.F }, 20); //keypress
            api.Wait(1000);

            LookAtSwitch2();

            //move to the Switch 2
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keydown

            //Wait until the player reaches the switch (Player HPath changes ater printing a new bot)
            while (!IsAt("//BP_Bot_C_1 ", switch2Pos, new Vector2(35, 65))) ;

            //Stop moving
            api.KeyPress(new KeyCode[] { KeyCode.W }, 0); //keyup

            //print a new bot (Press F)
            api.KeyPress(new KeyCode[] { KeyCode.F }, 20); //keypress
            api.Wait(1000);

            Vector3 newLeftDoorPos = api.GetObjectPosition("//BP_Door_C_UAID_B42E9936F5429ADA00_2086828164/fn:component('DoorLeft')");

            Assert.That(newLeftDoorPos.y, Is.Not.EqualTo(leftDoorPos.y), "Door not Open. Test failed!");
        }

        [Test, Order(0)]
        public void fanPuzzle()
        {

            api.Wait(5000);


            //Wait for the level to load
            api.WaitForObject("/*[@name = 'BP_PressurePlate_C_UAID_B42E9936F5429ADA00_2086838180']");
            api.Wait(1000);

            //Move Actor to the Button
            Vector3 switchPos = api.GetObjectPosition("/*[@name = 'BP_PressurePlate_C_UAID_B42E9936F5429ADA00_2086838180']");
            api.CallMethod("/*[@name = 'BP_Bot_C_0']", "K2_SetActorLocation", new object[] { switchPos });
            
            //Print a new Bot and assume control
            api.KeyPress(new KeyCode[] { KeyCode.F }, 1);
            api.Wait(1000);

            //Save the position of the fan.
            Vector3 fanPos = api.GetObjectPosition("/*[@name = 'BP_Fan_C_UAID_B42E9936F5429ADA00_2086843187'] ");

            //Move the bot(player) to the fan.
            api.CallMethod("/*[@name = 'BP_Bot_C_1']", "K2_SetActorLocation", new object[] { fanPos });
            api.Wait(1000);

            //Check if Fan moved Player upwards
            Assert.IsTrue((api.GetObjectPosition("/*[@name = 'BP_Bot_C_1']").z > api.GetObjectPosition("/*[@name = 'BP_Bot_C_0']").z), "Fan did not activate");
            api.Wait(1000);

            //Print a new Bot and assume control
            api.KeyPress(new KeyCode[] { KeyCode.F }, 5);
            api.Wait(1000);

        }

        



      

        #region HelperFunctions

        bool IsAt(string objectHPath, Vector3 targetPos, Vector2 proximity = null)
        {
            Vector3 objectPos = api.GetObjectPosition(objectHPath);

            return CheckXYPlaneProximity(objectPos, targetPos, proximity);
        }

        bool CheckXYPlaneProximity(Vector3 v1, Vector3 v2, Vector2 threshold = null)
        {
            if (threshold == null)
            {
                threshold = new Vector2(50, 50);
            }
            return MathF.Abs(v1.x - v2.x) <= threshold.x && MathF.Abs(v1.y - v2.y) <= threshold.y;
        }

        void LookAtCrates()
        {
            api.Wait(2646); // 95268
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95268
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 95269
            api.Wait(37); // 95274
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.07f)); // 95274
            api.Wait(39); // 95279
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.0f)); // 95279
            api.Wait(31); // 95283
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.07f)); // 95283
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95284
            api.Wait(9); // 95286
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95286
            api.Wait(9); // 95288
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95288
            api.Wait(9); // 95290
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95290
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95291
            api.Wait(49); // 95297
            api.Vector2InputEvent("Mouse2D", new Vector2(0.28f, 0.07f)); // 95297
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95298
            api.Wait(35); // 95303
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95303
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95304
            api.Wait(134); // 95316
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.07f)); // 95316
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95317
            api.Wait(178); // 95336
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95336
            api.Wait(31); // 95340
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 95340
            api.Wait(9); // 95342
            api.Vector2InputEvent("Mouse2D", new Vector2(0.28f, 0.07f)); // 95342
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.0f)); // 95343
            api.Wait(10); // 95345
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 95345
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.0f)); // 95346
            api.Wait(37); // 95351
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.07f)); // 95351
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.0f)); // 95352
            api.Wait(19); // 95355
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.07f)); // 95355
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.0f)); // 95356
            api.Wait(17); // 95359
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.07f)); // 95359
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.0f)); // 95360
            api.Wait(183); // 95380
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.07f)); // 95380
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.0f)); // 95381
            api.Wait(19); // 95384
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.07f)); // 95384
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.0f)); // 95385
            api.Wait(39); // 95390
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 95390
            api.Wait(1892); // 95588
        }

        void LookAtSwitch1()
        {
            api.Wait(2241); // 371383
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371383
            api.Wait(9); // 371385
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371385
            api.Wait(73); // 371394
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371394
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371395
            api.Wait(326); // 371431
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.14f, 0.07f)); // 371431
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371432
            api.Wait(208); // 371458
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.28f, 0.07f)); // 371458
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371459
            api.Wait(16); // 371462
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.28f, 0.07f)); // 371462
            api.Wait(9); // 371464
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.63f, 0.0f)); // 371464
            api.Wait(31); // 371468
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.28f, 0.07f)); // 371468
            api.Wait(8); // 371470
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.63f, 0.0f)); // 371470
            api.Wait(63); // 371478
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.56f, 0.07f)); // 371478
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371479
            api.Wait(45); // 371485
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.56f, 0.07f)); // 371485
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.28f, 0.0f)); // 371486
            api.Wait(339); // 371520
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.49f, -0.07f)); // 371520
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371521
            api.Wait(18); // 371524
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.28f, -0.07f)); // 371524
            api.Wait(75); // 371533
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371533
            api.Wait(30); // 371537
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.35f, -0.07f)); // 371537
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371538
            api.Wait(365); // 371579
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371579
            api.Wait(9); // 371581
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.21f, 0.0f)); // 371581
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371582
            api.Wait(55); // 371589
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371589
            api.Wait(106); // 371601
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.14f, -0.07f)); // 371601
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, 0.0f)); // 371602
            api.Wait(170); // 371621
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.14f, -0.07f)); // 371621
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, 0.0f)); // 371622
            api.Wait(9); // 371624
            api.Vector2InputEvent("Mouse2D", new Vector2(0.0f, -0.07f)); // 371624
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, 0.0f)); // 371625
            api.Wait(28); // 371629
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371629
            api.Wait(9); // 371631
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371631
            api.Wait(19); // 371634
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371634
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371635
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371636
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371637
            api.Wait(8); // 371639
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371639
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371640
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371641
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371642
            api.Wait(45); // 371648
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371648
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371649
            api.Wait(38); // 371654
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371654
            api.Wait(92); // 371664
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 371664
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371665
            api.Wait(276); // 371697
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, -0.07f)); // 371697
            api.Wait(9); // 371699
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 371699
            api.Wait(2751); // 372004
        }

        void LookAtSwitch2()
        {
            api.Wait(638); // 416735
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, -0.07f)); // 416735
            api.Wait(9); // 416737
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.0f)); // 416737
            api.Wait(102); // 416749
            api.Vector2InputEvent("Mouse2D", new Vector2(0.28f, 0.07f)); // 416749
            api.Vector2InputEvent("Mouse2D", new Vector2(0.35f, 0.0f)); // 416750
            api.Wait(28); // 416754
            api.Vector2InputEvent("Mouse2D", new Vector2(0.7f, 0.14f)); // 416754
            api.Vector2InputEvent("Mouse2D", new Vector2(0.28f, 0.0f)); // 416755
            api.Wait(26); // 416759
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 416759
            api.Wait(9); // 416761
            api.Vector2InputEvent("Mouse2D", new Vector2(0.21f, 0.0f)); // 416761
            api.Vector2InputEvent("Mouse2D", new Vector2(0.14f, 0.07f)); // 416762
            api.Vector2InputEvent("Mouse2D", new Vector2(0.07f, 0.0f)); // 416763
            api.Wait(202); // 416786
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 416786
            api.Wait(909); // 416884
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 416884
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 416885
            api.Wait(89); // 416896
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 416896
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 416897
            api.Wait(146); // 416914
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, -0.07f)); // 416914
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 416915
            api.Wait(627); // 416982
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, 0.14f)); // 416982
            api.Vector2InputEvent("Mouse2D", new Vector2(0.0f, 0.07f)); // 416983
            api.Wait(57); // 416990
            api.Vector2InputEvent("Mouse2D", new Vector2(-0.07f, 0.14f)); // 416990
            api.Vector2InputEvent("Mouse2D", new Vector2(0.0f, 0.07f)); // 416991
            api.Wait(43); // 416997
            api.Vector2InputEvent("Mouse2D", new Vector2(0f, 0f));//no data// 416997
            api.Wait(3305); // 417365
        }

        #endregion
    }
}
