using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Security.Policy;

public enum EGameState
{
    attract_screen,
    how_to_play,
    active,
    paused,
    game_over,
    leaderboards,
}

public class GameManager : MonoBehaviour
{



    private EGameState currentGameState;

    public float StartingGameTime = 180.0f;
    private float _fTimeRemaining;
    private int _iScore;
    private int _iDangerQuotientInternal;
    private bool bScoreLocked;

    public PlayerController thePlayer;

    [Space(18)]
    [Header("Gameplay")]
    public float SecondsLostOnJetHit = 10.0f;
    public float SecondsLostOnTankHit = 2.0f;

    [Space(18)]
    [Header("Ook Juice")]
    public Color color_Sky;
    public Color color_Ground;
    private Color color_CurrentBackground;

    [Space(18)]
    [Header("Treasure Location Spawning")]
    public Vector2 MinMax_TimeBetweenSpawns = new Vector2(45f,60f);
    public Vector2 MinMax_SpawnDistance = new Vector2(400f, 1200f);
    public GameObject Prefab_TreasureLoc;


    private float fTimeSinceLastSpawn;
    private float fNextSpawnTime;

    private List<TreasureLocationComponent> spawnedTreasureLocations;

    [Space(18)]
    [Header("Song And Dance")]
    public GameObject Prefab_GoldDebris;
    public bool bStartGameAssembly;
    public bool bTutorialMode;
    public bool bHasChangedShapesInTutorial;
    private float fTutorialTimer;

    private UI_Main ui_Main;
    private UI_GameOver ui_GameOver;
    private UI_Menu ui_Menu;
    private UI_HowToPlay ui_HowToPlay;
    private UI_Leaderboards ui_Leaderboards;

    private int iNumDreadnaughtsSpawned;

    public string strPlayerHighScoreName;

    public float TimeRemaining()
    {
        return _fTimeRemaining;
    }

    public int Score()
    {
        return _iScore;
    }

    void Awake()
    {
        spawnedTreasureLocations = new List<TreasureLocationComponent>();
        ui_Main = GameObject.Find("UI_Main").GetComponent<UI_Main>();
        ui_Menu = GameObject.Find("UI_Menu").GetComponent<UI_Menu>();
        ui_GameOver = GameObject.Find("UI_GameOver").GetComponent<UI_GameOver>();
        ui_HowToPlay = GameObject.Find("UI_HowToPlay").GetComponent<UI_HowToPlay>();
        ui_Leaderboards = GameObject.Find("UI_Leaderboards").GetComponent<UI_Leaderboards>();

        TM_LeaderboardManager.SetDreamloURLBase("CSccinfBJU-L0rBAnECBiwQ6dkUj6uy0uKrMwVSVKPQw");
    }

	// Use this for initialization
	void Start ()
	{
	    ChangeGameState(EGameState.attract_screen);
        color_CurrentBackground = color_Ground;
	}

    void GameStart()
    {
        if( thePlayer != null )
            thePlayer.gameObject.SetActive(true);
        else
            thePlayer = GameObject.Find("Player").GetComponent<PlayerController>();

        thePlayer.PrepareForNewGame();

        spawnedTreasureLocations = new List<TreasureLocationComponent>();
        
        _fTimeRemaining = StartingGameTime;
        _iScore = 0;
        bScoreLocked = false;
        _iDangerQuotientInternal = 1;
        iNumDreadnaughtsSpawned = 0;

        color_CurrentBackground = color_Ground;

        Vector3 vPlayer = thePlayer.transform.position;

        for (int t = 0; t < 3; t++)
        {
            SpawnSpecificCollectableAt("collectable_square", vPlayer += new Vector3(40, 0, 0), 1.0f + t*0.2f);
            SpawnSpecificCollectableAt("collectable_circle", vPlayer += new Vector3(-80, 0, 0), 2.0f + t * 0.2f);
            SpawnSpecificCollectableAt("collectable_triangle", vPlayer += new Vector3(0, 120, 0), 3.0f + t * 0.2f);
        }

        //Don't let the player move at game start, until assembled
        bStartGameAssembly = true;
        bTutorialMode = true;
        bHasChangedShapesInTutorial = false;
        fTutorialTimer = 10.0f;

        

        ui_Main.SetDisplayTime(_fTimeRemaining);
    }

	// Update is called once per frame
    void Update()
    {
        switch (currentGameState)
        {
            case EGameState.attract_screen:
                UpdateAttract();
                break;
            case EGameState.active:
                UpdateActive();
                break;
            case EGameState.game_over:
                UpdateGameOver();
                break;
            case EGameState.how_to_play:
                UpdateHowToPlay();
                break;
            case EGameState.leaderboards:
                UpdateLeaderboards();
                break;
        }

    }

    void UpdateHowToPlay()
    {

        if (Input.GetKeyDown(KeyCode.Escape))
        {
            ChangeGameState(EGameState.attract_screen);
        }
        else if (ui_HowToPlay.fDelayBeforeNextScreen <= 0.0f && Input.anyKey)
        {
            ChangeGameState(EGameState.active);
        } 
    }

    void UpdateLeaderboards()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
            ChangeGameState(EGameState.attract_screen);
        else if (Input.GetKeyDown(KeyCode.LeftArrow))
            ui_Leaderboards.OnClick_Back();
        else if (Input.GetKeyDown(KeyCode.RightArrow))
            ui_Leaderboards.OnClick_Forward();
    }

    void UpdateGameOver()
    {
        if( Input.GetKeyDown(KeyCode.Escape))
            Application.Quit();
        //else if (ui_GameOver.fDelayBeforePushButtonToPlayAgain <= 0.0f && Input.anyKey)
       // {
       // }
    }

    void UpdateAttract()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
            Application.Quit();
        else if (Input.GetKeyDown(KeyCode.L))
            ChangeGameState(EGameState.leaderboards);
        else if (Input.anyKey)
            ChangeGameState(EGameState.how_to_play);
    }

    void UpdateActive()
    {
	    if (bTutorialMode)
	    {
	        UpdateTutorialMode();
	    }
	    else
	    {
	        _fTimeRemaining -= Time.deltaTime;
	        UpdateTreasureLocations();
	    }

	    if (bStartGameAssembly)
	    {
	        bStartGameAssembly = thePlayer.NotStartGameAssembledYet();
	        if (!bStartGameAssembly)
	        {
                for (int t = 0; t < 50; t++)
                {
                    GameObject go = Instantiate(Prefab_GoldDebris);
                    go.transform.position = thePlayer.transform.position;
                    DebrisComponent dc = go.GetComponent<DebrisComponent>();
                    dc.Launch();
                }

	        }
	    }
	}

    void UpdateTutorialMode()
    {
        Vector3 vDistance = thePlayer.transform.position;
        vDistance.z = 0;
        if( vDistance.sqrMagnitude > 40.0f * 40.0f )
            EndTutorialMode();

        if (bHasChangedShapesInTutorial)
        {
            fTutorialTimer -= Time.deltaTime;
            if (fTutorialTimer <= 0.0f)
                EndTutorialMode();
        }
    }

    public bool ChangeGameState(EGameState newState)
    {
        if (newState == currentGameState)
            return true;

        if (currentGameState == EGameState.game_over)
        {
            ui_GameOver.TurnOff();
        }
        else if (currentGameState == EGameState.attract_screen)
        {
            ui_Menu.TurnOff();
        }
        else if (currentGameState == EGameState.active)
        {
            ui_Main.TurnOff();
        }
        else if (currentGameState == EGameState.how_to_play)
        {
            ui_HowToPlay.TurnOff();
        }
        else if (currentGameState == EGameState.leaderboards)
        {
            ui_Leaderboards.TurnOff();
        }

        switch (newState)
        {
            case EGameState.attract_screen:
                ui_Menu.TurnOn();
                break;
            case EGameState.active:
                ui_Main.TurnOn();
                GameStart();
                break;
            case EGameState.game_over:
                ui_GameOver.TurnOn();
                break;
            case EGameState.how_to_play:
                ui_HowToPlay.TurnOn();
                break;
            case EGameState.leaderboards:
                ui_Leaderboards.TurnOn();
                break;
        }

        currentGameState = newState;
        return true;
    }

    public int GetGameDangerQuotient()
    {
        int iNewQuotient = 1;
        if (_fTimeRemaining < 30.0f)
            iNewQuotient = 4;
        else if (_fTimeRemaining < 90.0f)
            iNewQuotient = 3;
        else if (_fTimeRemaining < 120.0f)
            iNewQuotient = 2;

        _iDangerQuotientInternal = Mathf.Max(_iDangerQuotientInternal, iNewQuotient);

        return _iDangerQuotientInternal;
        
    }

    void UpdateTreasureLocations()
    {
        bool bShouldSpawnNew = false;
        bool bShouldSpawnDreadnaught = false;

        if (iNumDreadnaughtsSpawned == 0 && GetGameDangerQuotient() >= 4)
        {
            bShouldSpawnNew = true;
            bShouldSpawnDreadnaught = true;
        }

        //uh lol no
        else if (spawnedTreasureLocations.Count >= 5)
            return;

        //if there aren't any, spawn one right away.
        if (spawnedTreasureLocations.Count < 1)
            bShouldSpawnNew = true;
        else
        {
            //tick time since last spawn up, tick it even harder
            //if the game is close to over
            fTimeSinceLastSpawn += Time.deltaTime;
            if (_fTimeRemaining < 120.0f)
                fTimeSinceLastSpawn += Time.deltaTime*2.0f;
            if (_fTimeRemaining < 60.0f)
                fTimeSinceLastSpawn += Time.deltaTime * 2.0f;
            if (_fTimeRemaining < 30.0f)
                fTimeSinceLastSpawn += Time.deltaTime * 2.0f;

            if (fTimeSinceLastSpawn > fNextSpawnTime)
            {
                bShouldSpawnNew = true;
                if (_fTimeRemaining < 30.0f && Random.value < 0.05f)
                    bShouldSpawnDreadnaught = true;
            }
        }

        if (bShouldSpawnNew)
        {
            fNextSpawnTime = Random.Range(MinMax_TimeBetweenSpawns.x, MinMax_TimeBetweenSpawns.y);
            fTimeSinceLastSpawn = 0.0f;

            //where is player?
            Vector3 vSpawnPos = thePlayer.transform.position;
            vSpawnPos.z = -1;

            Vector3 vDelta = Random.onUnitSphere;
            vDelta.z = 0;
            vDelta.Normalize();

            if (bShouldSpawnDreadnaught)
            {
                vSpawnPos += vDelta*100.0f;
                GameObject go = Instantiate(Resources.Load<GameObject>("prefabs/enemy_ground_dreadnought"));
                go.transform.position = vSpawnPos;
                iNumDreadnaughtsSpawned++;
            }
            else
            {
                vSpawnPos += vDelta * Random.Range(MinMax_SpawnDistance.x, MinMax_SpawnDistance.y);
                GameObject go = Instantiate(Prefab_TreasureLoc);
                go.transform.position = vSpawnPos;
                TreasureLocationComponent tlc = go.GetComponent<TreasureLocationComponent>();

                spawnedTreasureLocations.Add(tlc);
            }

        }
    }


    public void RemoveTreasureLocation(TreasureLocationComponent tlc)
    {
        if( spawnedTreasureLocations.Contains(tlc))
            spawnedTreasureLocations.Remove(tlc);
    }

    public void SpawnCollectablesFromTreasureHex(Vector3 vPos)
    {
        int iDieRoll = Random.Range(1,21);
        switch (GetGameDangerQuotient())
        {
            case 1:
                SpawnLootDQ1(vPos, iDieRoll);
                break;
            case 2:
                SpawnLootDQ2(vPos, iDieRoll);
                break;
            case 3:
                SpawnLootDQ3(vPos, iDieRoll);
                break;
            case 4:
                SpawnLootDQ4(vPos, iDieRoll);
                break;
        }
    }

    void SpawnLootDQ1(Vector3 vPos, int iDieRoll)
    {
        //coin
        if (iDieRoll <= 15)
            SpawnSpecificCollectableAt("collectable_coin", vPos, Random.Range(1, 4), 1.0f, 1.5f);
        else if( iDieRoll <= 17)
            SpawnSpecificCollectableAt("collectable_triangle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 18)
            SpawnSpecificCollectableAt("collectable_circle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 19)
            SpawnSpecificCollectableAt("collectable_square", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 20)
            SpawnSpecificCollectableAt("collectable_threesome", vPos, 1, 1.0f, 3.5f);

    }
    void SpawnLootDQ2(Vector3 vPos, int iDieRoll)
    {
        if (iDieRoll <= 14)
            SpawnSpecificCollectableAt("collectable_coin", vPos, Random.Range(5, 12), 1.0f, 2.5f);
        else if (iDieRoll <= 16)
            SpawnSpecificCollectableAt("collectable_triangle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 17)
            SpawnSpecificCollectableAt("collectable_circle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 18)
            SpawnSpecificCollectableAt("collectable_square", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 19)
            SpawnSpecificCollectableAt("collectable_threesome", vPos, 1, 3.0f, 3.5f);
        else if (iDieRoll == 20)
            SpawnSpecificCollectableAt("collectable_time", vPos, 1, 2.0f, 2.5f);

    }
    void SpawnLootDQ3(Vector3 vPos, int iDieRoll)
    {
        if (iDieRoll <= 12)
            SpawnSpecificCollectableAt("collectable_coin", vPos, Random.Range(15, 25), 1.0f, 3.5f);
        else if (iDieRoll <= 14)
            SpawnSpecificCollectableAt("collectable_triangle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll <= 16)
            SpawnSpecificCollectableAt("collectable_circle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll <= 18)
            SpawnSpecificCollectableAt("collectable_square", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll == 19)
            SpawnSpecificCollectableAt("collectable_threesome", vPos, 1, 3.0f, 3.5f);
        else if (iDieRoll == 20)
            SpawnSpecificCollectableAt("collectable_time", vPos, Random.Range(1, 3), 2.0f, 2.5f);
    }
    void SpawnLootDQ4(Vector3 vPos, int iDieRoll)
    {
        if (iDieRoll <= 12)
            SpawnSpecificCollectableAt("collectable_coin", vPos, Random.Range(35, 50), 1.0f, 4.5f);
        else if (iDieRoll <= 13)
            SpawnSpecificCollectableAt("collectable_triangle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll <= 14)
            SpawnSpecificCollectableAt("collectable_circle", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll <= 15)
            SpawnSpecificCollectableAt("collectable_square", vPos, 1, 1.0f, 1.5f);
        else if (iDieRoll <= 17)
            SpawnSpecificCollectableAt("collectable_threesome", vPos, 1, 3.0f, 3.5f);
        else if (iDieRoll <= 20)
            SpawnSpecificCollectableAt("collectable_time", vPos, Random.Range(1, 4), 2.0f, 2.5f);
    }

    public void SpawnSpecificCollectableAt(string strName, Vector3 vLocation, int iQuantity, float fTimeMin, float fTimeMax)
    {
        for (int t = 0; t < iQuantity; t++)
        {
            SpawnSpecificCollectableAt(strName, vLocation, Random.Range(fTimeMin, fTimeMax));
        }
    }

    public void SpawnSpecificCollectableAt(string strName, Vector3 vLocation, float fDesiredTime)
    {
        GameObject go = Instantiate(Resources.Load<GameObject>("prefabs/" + strName ));
        go.transform.position = vLocation;
        CollectableComponent cc = go.GetComponent<CollectableComponent>();
        cc.LaunchCollectable(Vector3.zero, 15.0f, fDesiredTime);
    }

    public Vector3 GetLocationNearLocation(Vector3 vNearHere, float fMinRange, float fMaxRange)
    {
        Vector3 vDirection = Random.onUnitSphere;
        vDirection.z = 0;
        vDirection.Normalize();
        return vNearHere + (vDirection*Random.Range(fMinRange, fMaxRange));
    }

    public Vector3 GetLocationNearLocation(Vector3 vNearHere, Vector2 vRange)
    {
        return GetLocationNearLocation(vNearHere, vRange.x, vRange.y);
    }

    public void SpawnBaddiesNearTreasure(Vector3 vLocation)
    {
        int iDangerQ = GetGameDangerQuotient();
        //some number of siege cannons
        int iCannonCount = 0;

        if (iDangerQ > 2)
            iCannonCount = iDangerQ + (iDangerQ/2);


        for (int t = 0; t < iCannonCount; t++)
        {
            Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 15.0f, 25.0f);
            GameObject newSiege = Instantiate(Resources.Load<GameObject>("prefabs/enemy_ground_siegetank"));
            newSiege.transform.position = vSpawnPos;
        }

        //some number of trash ground mobs
        if (iDangerQ > 1)
        {
            //tanks!
            int iNumTanks = (iDangerQ - 1) + (Random.Range(1, 3)*(iDangerQ - 2));
            for (int t = 0; t < iNumTanks; t++)
            {
                Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 15, 30.0f);
                GameObject spidar = Instantiate(Resources.Load<GameObject>("prefabs/enemy_ground_tank"));
                spidar.transform.position = vSpawnPos;
            }
        }

        //spiders!
        int iNumSpiders = 3 + (Random.Range(1, 3)*iDangerQ);
        for (int t = 0; t < iNumSpiders; t++)
        {
            Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 5, 12.0f);
            GameObject spidar = Instantiate(Resources.Load<GameObject>("prefabs/enemy_ground_spider"));
            spidar.transform.position = vSpawnPos;
        }

        //late game? Maybe a boss dreadnought!~

        //some number of sky enemies!

        //bats
        int iNumBats = 5 + (Random.Range(6, 8) * (iDangerQ - 1));
        for (int t = 0; t < iNumSpiders; t++)
        {
            Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 5, 30.0f);
            GameObject bat = Instantiate(Resources.Load<GameObject>("prefabs/enemy_flying_bat"));
            bat.transform.position = vSpawnPos;
        }

        //gunships
        if (iDangerQ > 1)
        {
            int iNumGunship = Random.Range(iDangerQ, iDangerQ*iDangerQ);
            for (int t = 0; t < iNumGunship; t++)
            {
                Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 2f, 3f);
                GameObject bat = Instantiate(Resources.Load<GameObject>("prefabs/enemy_purple_gunship"));
                bat.transform.position = vSpawnPos;
            }
        }

        //loomers
        if (iDangerQ > 2)
        {
            int iNumLoomers = iDangerQ - 2;
            for (int t = 0; t < iNumLoomers; t++)
            {
                Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 20f, 40f);
                GameObject bat = Instantiate(Resources.Load<GameObject>("prefabs/enemy_purple_loomer"));
                bat.transform.position = vSpawnPos;
            }
        }

        //flying base
        if (iDangerQ > 3)
        {
            Vector3 vSpawnPos = GetLocationNearLocation(vLocation, 2, 3);
            GameObject bat = Instantiate(Resources.Load<GameObject>("prefabs/enemy_purple_base"));
            bat.transform.position = vSpawnPos;
        }
    }

    public void AddScore(int iNewValue)
    {
        if (bScoreLocked)
            return;

        //math math math
        _iScore += iNewValue;
    }

    public void AddTime(float fMoarTime)
    {
        _fTimeRemaining += fMoarTime;
        ui_Main.SetTimerAdjustText("+" + (int)fMoarTime + " second" + (fMoarTime > 1 ? "s" : ""));
    }

    public void EndTutorialMode()
    {
        ui_Main.fPostTutorialLastTipTimer = 10.0f;
        bTutorialMode = false;
    }

    public void TransitionToGround(float fTime)
    {
        StartCoroutine(BackgroundTransitionCoroutine(color_Sky, color_Ground, fTime));
    }

    public void TransitionToSky(float fTime)
    {
        StartCoroutine(BackgroundTransitionCoroutine(color_Ground, color_Sky, fTime));
    }

    IEnumerator BackgroundTransitionCoroutine(Color start, Color end, float fTransTime)
    {
        float fTime = 0;

        while (fTime < fTransTime)
        {
            color_CurrentBackground = Color.Lerp(start, end, fTime/fTransTime);
            fTime += Time.deltaTime;
            yield return null;
        }

        color_CurrentBackground = end;
    }

    public Color GetBackgroundColor()
    {
        return color_CurrentBackground;
    }

    public string GetTankWisdom()
    {
        return "Slow bruiser, lose " + (int)SecondsLostOnTankHit + "s when hit\nDestroys ground targets, knocks back air\nRight Stick aims turret";
    }

    public string GetJetWisdom()
    {
        return "Hella fast, lose " + (int)SecondsLostOnJetHit +"s when hit\nDestroys air targets\nRight Stick blocks shots, charges laser";
    }

    public void PlayerWasHit()
    {
        float fLoss = thePlayer.currentForm == EShipForm.triangle ? SecondsLostOnJetHit : SecondsLostOnTankHit;
        _fTimeRemaining -= fLoss;
        ui_Main.SetTimerAdjustText("-" + (int)fLoss + " second" + (fLoss > 1 ? "s" : ""));

    }

    public void GameOver()
    {
        if (currentGameState == EGameState.game_over)
            return;

        //lock the score down
        bScoreLocked = true;


        //blow up all enemies
        EnemyAI[] slowAssEnemySearch = FindObjectsOfType<EnemyAI>();
        for (int t = 0; t < slowAssEnemySearch.Length; t++)
        {
            EnemyAI e = slowAssEnemySearch[t];
            e.OnKilled();
        }

        //blow up all projectiles
        TM_Projectile[] slowAssFindCall = FindObjectsOfType<TM_Projectile>();
        for (int t = 0; t < slowAssFindCall.Length; t++)
        {
            TM_Projectile p = slowAssFindCall[t];
            Destroy(p.gameObject, 0.5f);
        }


        TreasureLocationComponent[] tlc = FindObjectsOfType<TreasureLocationComponent>();
        for (int t = 0; t < tlc.Length; t++)
        {
            TreasureLocationComponent e = tlc[t];
            Destroy(e.gameObject);
        }

        BreakableTreasureHex[] bth = FindObjectsOfType<BreakableTreasureHex>();
        for (int t = 0; t < bth.Length; t++)
        {
            BreakableTreasureHex e = bth[t];
            Destroy(e.gameObject);
        }

        //nullify the player
        thePlayer.gameObject.SetActive(false);

        ChangeGameState(EGameState.game_over);
    }
}
