﻿using System;
using System.Diagnostics;
using NUnit.Framework;
using gdio.unity_api;
using gdio.unity_api.v2;
using gdio.unity_api.utilities;
using gdio.common.objects;

namespace _2DTest
{
    [TestFixture]
    public class UnitTest
    {
        //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 pathToExe = TestContext.Parameters.Get("pathToExe", null);

        ApiClient api;

        [OneTimeSetUp]
        public void Connect()
        {
            try
            {
                api = new ApiClient();

                if (pathToExe != null)
                {
                    ApiClient.Launch(pathToExe);
                    api.Connect("localhost", 19734, false, 30);
                }

                else if (testMode == "IDE")
                {
                    api.Connect("localhost", 19734, true, 30);
                }
                else api.Connect("localhost", 19734, false, 30);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            api.EnableHooks(HookingObject.MOUSE);
            api.EnableHooks(HookingObject.KEYBOARD);

            //Start the Game
            api.WaitForObject("//*[@name='StartButton']");
            api.ClickObject(MouseButtons.LEFT, "//*[@name='StartButton']", 30);
            api.Wait(3000);
        }

        [Test]
        public void Zone1()
        {
            string infoPostNum = "//*[@name='InfoPost'][1]"; //Some versions of this game have required the infoPost instance number to be changed

            api.Wait(4000);
            api.WaitForObject(infoPostNum);

            //Find the target infopost
            var infoPost = api.GetObjectPosition(infoPostNum);
            Console.WriteLine("InfoPost x:" + infoPost);    

            //While we're in zone1, keep trying to find the InfoPost
            while (api.GetSceneName() == "Zone1")
            {
                //If we're near the post, down-jump
                if (api.GetObjectPosition("/Player[@name='Ellen']").x >= infoPost.x - 1 && api.GetObjectPosition("/Player[@name='Ellen']").x <= infoPost.x + 1)
                {
                    Console.WriteLine("InfoPost found!");
                    api.Wait(3000);
                    api.KeyPress(new KeyCode[] { KeyCode.S }, (ulong)api.GetLastFPS());
                    api.Wait(500);
                    api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 2);
                }

                //If we're on the left of the gap, find a way over
                while (api.GetObjectPosition("/Player[@name='Ellen']").x < 10 && api.GetObjectPosition("/Player[@name='Ellen']").y < -1)
                {
                    //Jump the chasm. This is tricky, so it might take a few tries
                    Console.WriteLine("We're going to jump!");
                    api.KeyPress(new KeyCode[] { KeyCode.D }, (ulong)api.GetLastFPS() * 2); //Move right for ~2 seconds
                    api.Wait(500);
                    api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 2); //Jump for a 1/2 second
                    api.Wait(500);
                }

                //If we fall in the hole, need to back up and try again
                while (api.GetObjectPosition("/Player[@name='Ellen']").x >= 10 && api.GetObjectPosition("/Player[@name='Ellen']").y <= -3)
                {
                    Console.WriteLine("We're stuck in the hole, please stand by!");
                    api.KeyPress(new KeyCode[] { KeyCode.A }, (ulong)api.GetLastFPS()); //Move left for 1 second
                    api.Wait(500);
                    api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 4); //Jump for a 1/4 second
                    api.Wait(250);
                }

                //Once we have crosses the gap, move right
                while (api.GetObjectPosition("/Player[@name='Ellen']").x > 13 && api.GetObjectPosition("/Player[@name='Ellen']").x < infoPost.x && api.GetObjectPosition("/Player[@name='Ellen']").y > -2)
                {
                    Console.WriteLine("x:" + api.GetObjectPosition("/Player[@name='Ellen']").x + " < " + infoPost + ". Moving Right.");
                    api.KeyPress(new KeyCode[] { KeyCode.D }, (ulong)api.GetLastFPS() / 3);
                    api.Wait(333);
                }

                //If Ellen is to the right of the InfoPost, move left
                while (api.GetObjectPosition("/Player[@name='Ellen']").x > infoPost.x)
                {
                    Console.WriteLine("x:" + api.GetObjectPosition("/Player[@name='Ellen']").x + " > " + infoPost + ". Moving Left.");
                    api.KeyPress(new KeyCode[] { KeyCode.A }, (ulong)api.GetLastFPS() / 3);
                    api.Wait(333);
                }
            }

            api.Wait(3000);
            Assert.AreEqual(api.GetSceneName(), "Zone2", "Zone 2 not loaded!");
        }

        [Test]
        public void Zone2()
        {
            Console.WriteLine("Waiting for Key");
            api.WaitForObject("/Objective[@name='Key' and ./fn:component('UnityEngine.Behaviour')/@isActiveAndEnabled = 'True']");
            api.Wait(3000);

            var fps = api.GetLastFPS();
            Console.WriteLine("FPS: " + fps);

            //The goal in zone 2 is the first key
            var keyVector = api.GetObjectPosition("/Objective[@name='Key' and ./fn:component('UnityEngine.Behaviour')/@isActiveAndEnabled = 'True']");
            var ellen = api.GetObjectPosition("/Player[@name='Ellen']");
            Console.WriteLine("Ellen coordinates:" + ellen.x + "," + ellen.y);
            Console.WriteLine("Key coordinates:" + keyVector.x + "," + keyVector.y);

            Console.WriteLine("Key Color: " + api.GetObjectFieldValue<Color>("/*[@name='KeyCanvas']/*[@name='KeyIcon(Clone)'][0]/*[@name='Key']/fn:component('UnityEngine.UI.Image')/@color").ToString());

            api.Wait(2000);
            
            while (api.GetObjectFieldValue<Color>("/*[@name='KeyCanvas']/*[@name='KeyIcon(Clone)'][0]/*[@name='Key']/fn:component('UnityEngine.UI.Image')/@color").ToString().Equals("RGBA(1.000, 1.000, 1.000, 0.000)"))
            {
                //Check if we're on either side of the key
                while ((api.GetObjectPosition("/Player[@name='Ellen']").x != keyVector.x) & (api.GetObjectPosition("/Player[@name='Ellen']").y != keyVector.y))
                {
                    //If we've hit the key, break
                    if (api.GetObjectFieldValue<Color>("/*[@name='KeyCanvas']/*[@name='KeyIcon(Clone)'][0]/*[@name='Key']/fn:component('UnityEngine.UI.Image')/@color").ToString().Equals("RGBA(1.000, 1.000, 1.000, 1.000)"))
                    {
                        api.CaptureScreenshot("Zone2.jpg",false, true);
                        Console.WriteLine("Key Get!");
                        api.Wait(1000);
                        Assert.IsTrue(api.GetObjectFieldValue<Color>("/*[@name='KeyCanvas']/*[@name='KeyIcon(Clone)'][0]/*[@name='Key']/fn:component('UnityEngine.UI.Image')/@color").ToString().Equals("RGBA(1.000, 1.000, 1.000, 1.000)"));
                        break;
                    }
                    else
                    {
                        //If we're to the left, move right
                        while (api.GetObjectPosition("/Player[@name='Ellen']").x < keyVector.x)
                        {
                            api.KeyPress(new KeyCode[] { KeyCode.D }, (ulong)api.GetLastFPS());
                            api.Wait(500);

                            // While moving right, if we're lower than the key, jump
                            if (api.GetObjectPosition("/Player[@name='Ellen']").y < keyVector.y)
                            {
                                api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 3);
                            }
                        }

                        //If we're to the right, move left
                        while (api.GetObjectPosition("/Player[@name='Ellen']").x > keyVector.x)
                        {
                            api.KeyPress(new KeyCode[] { KeyCode.A }, (ulong)api.GetLastFPS());
                            api.Wait(500);

                            // While moving left, if we're lower than the key, jump
                            if (api.GetObjectPosition("/Player[@name='Ellen']").y < keyVector.y)
                            {
                                api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 3);
                            }
                        }
                    }
                }
            }

            //Move to the next level entrance
            while (api.GetSceneName() != "Zone3")
            {
                api.KeyPress(new KeyCode[] { KeyCode.D }, (ulong)api.GetLastFPS());
                api.Wait(500);
                api.KeyPress(new KeyCode[] { KeyCode.Space }, (ulong)api.GetLastFPS() / 2);
            }
            api.Wait(6000);

            //If the jump-down was successful, we should be in Zone3
            Assert.AreEqual(api.GetSceneName(), "Zone3");
            
        }

        [OneTimeTearDown]
        public void Disconnect()
        {
            //Disconnect and close GDIO
            api.DisableHooks(HookingObject.ALL);
            api.Wait(2000);
            api.Disconnect();
            api.Wait(2000);
        }
    }
}
