using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Class for dealing with the debug console and its commands/cheats
/// </summary>
public class DebugConsoleManager : MonoBehaviour
{
    private bool _toggleWindow = false;
    private string _commandIn;

    // List to hold previously used commands so we can go up and down them.
    private List<string> _prevCommands = new List<string>();
    private List<string> _commandOutput = new List<string>();
    private int _prevCommandIndex = 0;

    /// <summary>
    /// Method to toggle the debug console
    /// </summary>
    public void ToggleDebugConsole(bool force = false)
    {         
        if (_toggleWindow && !force)
        {
            Time.timeScale = 1.0f;
            _toggleWindow = false;
        }
        else
        {
            Time.timeScale = 0.0f;
            _toggleWindow = true;
        }
    }

    private void OnGUI()
    {
        if (!_toggleWindow)
        {            
            return;
        }
        
        // Draw the GUI box for the console box at the top of the screen
        GUI.Box(new Rect(0, 0, Screen.width, 30), "");
        GUI.backgroundColor = new Color(0, 0, 0, 0);

        // This block checks for keypresses by the user whilst the GUI is shown since the actual game time is paused
        Event currentEvent = Event.current;
        if (currentEvent.type == EventType.KeyDown)
        {
            switch (currentEvent.keyCode)
            {
                // just close the menu if the tilde or backquote key is pushed
                case KeyCode.Tilde:
                case KeyCode.BackQuote:
                    GUI.FocusControl(null);
                    ToggleDebugConsole();
                    return;
                // Up gets the last command in the list
                case KeyCode.UpArrow:
                    if (_prevCommands.Count > 0)
                    {
                        _commandIn = _prevCommands[_prevCommandIndex];
                        if (_prevCommandIndex > 0)
                        {
                            _prevCommandIndex -= 1;
                        }
                    }
                    break;
                // Down gets the next command in the list
                case KeyCode.DownArrow:
                    if (_prevCommands.Count > 0)
                    {
                        _commandIn = _prevCommands[_prevCommandIndex];
                        if (_prevCommandIndex < _prevCommands.Count - 1)
                        {
                            _prevCommandIndex += 1;
                        }
                    }
                    break;
                // Return parses the command
                case KeyCode.Return:
                    GUI.FocusControl(null);
                    ToggleDebugConsole();
                    if (_commandIn.Trim().Length > 0)
                    {
                        ParseCommand(_commandIn.Trim());
                        _prevCommands.Add(_commandIn);
                        _prevCommandIndex = _prevCommands.Count - 1;
                    }
                    _commandIn = "";                    
                    return;
            }

        }        

        // Wait for user input
        GUI.SetNextControlName("DebugConsole");
        _commandIn = GUI.TextField(new Rect(10.0f, 5.0f, Screen.width - 20.0f, 20.0f), _commandIn);
        GUI.FocusControl("DebugConsole");

    }

    /// <summary>
    /// Method to parse the given command and run it
    /// </summary>
    /// <param name="currentCommand">Command string to parse</param>
    private void ParseCommand(string currentCommand)
    {        
        // Split the command by its space characters, first command part should always be the actual command with parameters following
        string[] commandParts = currentCommand.ToLower().Split(' ');
        switch(commandParts[0])
        {
            // Suicide player
            case "die":
            case "kilme":
                KillPlayer();
                break;
            // Heal the player back to full lives
            case "fullheal":
            case "pillshere":
                HealPlayer();
                break;
            // God Mode player
            case "god":
                GodModeToggle();
                break;
            // Turn off gravity for the player, will probs change to keep the player x height above the blackhole since you cant jump down
            case "nosuck":
                TogglePlayerGravity();
                break;
            case "freezeall":
                ToggleFreezeAll();
                break;
            // Adds X amount of cores to the player
            case "addcore":
                int pickupAmount = 1;
                if (commandParts.Length > 1)
                {
                    int.TryParse(commandParts[1], out pickupAmount);
                }
                AddCore(pickupAmount);
                break;
            // Next Level
            case "levelup":
                DoLevelJump(1);
                break;
            case "setlevel":
                int levelJump = 0;
                if (commandParts.Length > 1)
                {
                    int.TryParse(commandParts[1], out levelJump);
                }
                DoLevelJump(levelJump);
                break;
            case "imreallybadatthisgame":
                DoLevelJump(100);
                break;
            // Pick up all current cores floating in the scene
            case "pickupall":
                PickupAll();
                break;
            case "fall":
                DoFall();
                break;
            // Kill all of the current enemies on the scene
            case "killall":
                KillAllEnemies();
                break;
            case "spawn":
                if (commandParts.Length > 1)
                {
                    string spawnType = commandParts[1];
                    int amountToSpawn = 1;
                    if (commandParts.Length > 2)
                    {
                        int.TryParse(commandParts[2], out amountToSpawn);
                    }
                    int randomSpawnLoc = 0;
                    if (commandParts.Length > 3)
                    {
                        int.TryParse(commandParts[3], out randomSpawnLoc);
                    }
                    DoSpawn(spawnType, amountToSpawn, randomSpawnLoc);
                }
                break;
        }
    }

    private void DoFall()
    {
        GameObject[] enemiesSpawned = GameObject.FindGameObjectsWithTag("Enemy");
        foreach (GameObject enemy in enemiesSpawned)
        {
            EnemyObjectShared enemyOS = enemy.GetComponent<EnemyObjectShared>();
            if (enemyOS != null)
            {
                enemyOS.DoFailure();
            }
        }
    }

    private void DoSpawn(string spawnType, int spawnAmount, int spawnRandomLoc)
    {
        int spawnPrefabId = -1;
        switch (spawnType)
        {
            case "dumb":
                spawnPrefabId = 0;
                break;
            case "coward":
                spawnPrefabId = 1;
                break;
            case "chase":
                spawnPrefabId = 2;
                break;
            case "drop":
                spawnPrefabId = 3;
                break;
            case "mine":
                spawnPrefabId = 4;
                break;
            case "rock":
                spawnPrefabId = 5;
                break;
            case "core":
                spawnPrefabId = 6;
                break;
            case "health":
                spawnPrefabId = 7;
                break;
            case "shield":
                spawnPrefabId = 8;
                break;
        }

        if (spawnPrefabId != -1)
        {
            DebugSpawning debugSpawner = GameObject.Find("DebugSpawner").GetComponent<DebugSpawning>();
            if (debugSpawner != null)
            {
                bool randomSpawnLocBool = false;
                if (spawnRandomLoc == 1)
                {
                    randomSpawnLocBool = true;
                }
                for (int i = 0; i < spawnAmount; i++)
                {
                    debugSpawner.DoADebugSpawn(spawnPrefabId, randomSpawnLocBool);
                }
            }
        }
    }

    private void DoLevelJump(int levelsToJump)
    {
        if (levelsToJump > 0)
        {
            GameManager.Instance.level = GameManager.Instance.level + levelsToJump;
        }
    }

        private void ToggleFreezeAll()
    {
        GameObject[] currentEnemies = GameObject.FindGameObjectsWithTag("Enemy");
        foreach (GameObject currentEnemy in currentEnemies)
        {
            HoverMovement currentEnemyMovement = currentEnemy.GetComponent<HoverMovement>();
            if (currentEnemyMovement != null)
            {
                currentEnemyMovement.freezeMovement = !currentEnemyMovement.freezeMovement;
            }
        }
    }

    private void TogglePlayerGravity()
    {
        PlayerBlackHoleForce currentPlayerBHF = GameObject.Find("Player").GetComponent<PlayerBlackHoleForce>();
        if (currentPlayerBHF != null)
        {
            currentPlayerBHF.blackHolePullImmunue = !currentPlayerBHF.blackHolePullImmunue;
        }
    }

    /// <summary>
    /// Method to toggle god mode
    /// </summary>
    private void GodModeToggle()
    {
        GameManager.Instance.godMode = !GameManager.Instance.godMode;
    }

    /// <summary>
    /// Method to heal the player to full lives
    /// </summary>
    private void HealPlayer()
    {
        GameManager.Instance.lives = 10;
    }

    /// <summary>
    /// Method to kill the player and lose a life
    /// </summary>
    private void KillPlayer()
    {
        PlayerColliderManager playerCM = GameObject.Find("Player").GetComponent<PlayerColliderManager>();
        if (playerCM != null)
        {
            playerCM.TriggerDeath();
        }
    }

    /// <summary>
    /// Method for adding X amount of cores
    /// </summary>
    /// <param name="amount">Amount of cores to add</param>
    private void AddCore(int amount)
    {
        for (int i = 0; i < amount; i++)
        {
            GameManager.Instance.AddCore();
        }        
    }

    /// <summary>
    /// Method to pick up all of the currently active cores floating on the scene
    /// </summary>
    private void PickupAll()
    {
        GameObject[] currentCores = GameObject.FindGameObjectsWithTag("Core");
        foreach (GameObject currentCore in currentCores)
        {
            CoreColliderManager currentCoreCollider = currentCore.GetComponent<CoreColliderManager>();
            if (currentCoreCollider != null)
            {
                currentCoreCollider.PickupCore();
            }
        }
    }

    /// <summary>
    /// Method to kill all of the current enemies
    /// </summary>
    private void KillAllEnemies()
    {
        GameObject[] currentEnemies = GameObject.FindGameObjectsWithTag("Enemy");
        foreach (GameObject currentEnemy in currentEnemies)
        {
            ColliderManager currentEnemyCollider = currentEnemy.GetComponent<ColliderManager>();
            if (currentEnemyCollider != null)
            {
                currentEnemyCollider.TriggerDeath();
            }            
        }

    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.BackQuote) || Input.GetKeyDown(KeyCode.Tilde))
        {   
            ToggleDebugConsole(true);
        }            
    }
}
