using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine;
using DG.Tweening;

public class GameController : MonoBehaviour 
{
    private int _tutorialCorrectCount = 0;
    private int _tutorialCorrectMax = 2;

    public static GameController Instance;

    public static float _maxDistFromPGrab = 2.3f;

    public ColorPreset[] PortalColorPresets;
    
    [SerializeField] private GameObject _fireDudePrefab;
    [SerializeField] private GameObject _enemyPrefab;
    [SerializeField] private GameObject _bombPrefab;
    [SerializeField] private GameObject[] _packagePrefabs;

    private Player _player;
    [SerializeField] private SuitDudeController _suits;

    [Header("UI")]
    [SerializeField] private CanvasGroup _canvasGroup;
    [SerializeField] private Button _tryAgain;

    private float _nextSpawn = 0;
    private Vector2 _minMaxSpawnDelay = new Vector2(1.5f, 6.4f);

    [HideInInspector] public Portal[] Portals;
    private List<Portal> _entryPortals = new List<Portal>();

    [HideInInspector] public List<Package> Packages = new List<Package>();

    private float _satisfaction = 1f;
    private bool _gameOver = false;

    void Awake()
    {
        Instance = this;
        PortalSetup();

        _player = FindObjectOfType<Player>();
    }

	// Use this for initialization
	void Start () 
    {
        Cursor.lockState = CursorLockMode.Confined;
        _nextSpawn = 3f;

        _tryAgain.onClick.AddListener(ResetScene);

        SendPortalsToRenderers();
	}

    private void ResetScene()
    {
        SceneManager.LoadScene(1);
    }
	
    void Update()
    {

        if (_player.transform.position.y < -10)
            GameOver();

        if (InTutorial())
        {
            if(Packages.Count == 0)
            {
                SpawnPackage();
            }
        }
        else
        {
            _satisfaction = Mathf.Clamp01(_satisfaction);
            _satisfaction -= Time.deltaTime * 0.024f;

            if (_satisfaction < 0)
                 GameOver();


            if (!_gameOver && Time.time > _nextSpawn)
            {
                _nextSpawn = Time.time + Random.Range(_minMaxSpawnDelay.x, _minMaxSpawnDelay.y);


                List<int> indexes = GetOpenPortalIndexList();
                int chosenPortalIndex = indexes[Random.Range(0, indexes.Count)];
                Portal chosenPortal = Portals[chosenPortalIndex];

                //int destinationPortal = indexes[Random.Range(0, indexes.Count)];

                //Portal chosenPortal = _entryPortals[Random.Range(0, _entryPortals.Count)];
                //int chosenPortalIndex = chosenPortal.GetID();

                float spawnPackage = Random.Range(0, 100f);
                if (spawnPackage < 70)
                {
                    int loop = 1;
                    if (Random.value < 0.15f)
                        loop = 8;

                    for (int i = 0; i < loop; i++)
                    {
                        SpawnPackage(chosenPortalIndex);
                    }
                }
                else
                {
                    if (Random.value < 0.2f)
                        Portals[chosenPortalIndex].SpawnDude(_fireDudePrefab);
                    else
                    {
                        if (Random.value > 0.35f)
                            Portals[chosenPortalIndex].Spawn(_enemyPrefab);
                        else
                            Portals[chosenPortalIndex].Spawn(_bombPrefab);
                    }
                }
            }
        }

        FixUI();
    }

    private void SpawnPackage(int chosenPortalIndex = -1)
    {
        if(chosenPortalIndex == -1)
        {
            List<int> indexes = GetOpenPortalIndexList();
            chosenPortalIndex = indexes[Random.Range(0, indexes.Count)];
        }

        List<int> indexes2 = GetOpenPortalIndexList();
        indexes2.Remove(chosenPortalIndex);

        int destinationPortal = indexes2[Random.Range(0, indexes2.Count)];

        var package = _packagePrefabs[Random.Range(0, _packagePrefabs.Length)];
        Portals[chosenPortalIndex].Spawn(package, destinationPortal);
    }

    private List<int> GetPortalIndexList()
    {
        List<int> indexes = new List<int>();
        for (int i = 0; i < Portals.Length; i++)
            indexes.Add(i);

        return indexes;
    }

    private List<int> GetOpenPortalIndexList()
    {
        List<int> indexes = new List<int>();
        for (int i = 0; i < Portals.Length; i++)
            if (Portals[i].Open)
                indexes.Add(i);

        return indexes;
    }

    private List<int> GetClosedPortalIndexList()
    {
        List<int> indexes = new List<int>();
        for (int i = 0; i < Portals.Length; i++)
            if(Portals[i].Open == false)
                indexes.Add(i);

        return indexes;
    }

    private void FixUI()
    {
        UIController.Instance.SetSatisfaction(_satisfaction);
    }

    public void PackageCorrect()
    {
        if (!InTutorial())
            _satisfaction += 0.2f;

        SoundController.Instance.PlaySound(SoundController.Instance._succes);
        UIController.Instance.Correct();
        _suits.DoReaction(true);

        _tutorialCorrectCount++;
    }

    public void PackageFailed()
    {
        if (!InTutorial())
            _satisfaction -= 0.04f;

        SoundController.Instance.PlaySound(SoundController.Instance._fail);
        UIController.Instance.Fail();
        _suits.DoReaction(false);
    }

    private bool InTutorial()
    {
        return _tutorialCorrectCount < _tutorialCorrectMax;
    }

    private void PortalSetup()
    {
        Portals = FindObjectsOfType<Portal>();
        for (int i = 0; i < Portals.Length; i++)
            _entryPortals.Add(Portals[i]);

        List<int> indexes = GetPortalIndexList();

        Portal[] _organizedPortals = new Portal[Portals.Length];
        for (int i = 0; i < Portals.Length; i++)
        {
            int index = Random.Range(0, indexes.Count - 1);
            index = indexes[index];

            indexes.Remove(index);

            Portals[i].Setup(index);
            _organizedPortals[index] = Portals[i];
        }

        Portals = _organizedPortals;


        ActivateRandomPortal();
        ActivateRandomPortal();
        ActivateRandomPortalOnGoing();  
    }


    private void ActivateRandomPortalOnGoing()
    {
        if (InTutorial() == false)
        {
            List<int> indexes = GetClosedPortalIndexList();
            if (indexes.Count == 0)
                return;

            int index = indexes[Random.Range(0, indexes.Count - 1)];
            Portals[index].Activate(true);
        }

        Invoke("ActivateRandomPortalOnGoing", Random.Range(3, 7));
    }

    private void ActivateRandomPortal()
    {
        List<int> indexes = GetClosedPortalIndexList();
        int index = indexes[Random.Range(0, indexes.Count - 1)];
        
        Portals[index].Activate(true);
    }


    private List<Renderer> _signedUpPortalRenderers = new List<Renderer>();
    public void SetupMaterialForPortals(Renderer renderer)
    {
        _signedUpPortalRenderers.Add(renderer);
        SendPortalsToRenderer(renderer);
    }


    public void SendPortalsToRenderers()
    {
        // Clean up
        List<Renderer> _nullRenderers = new List<Renderer>();
        for (int i = 0; i < _signedUpPortalRenderers.Count; i++)
        {
            if(_signedUpPortalRenderers[i] == null)
                _nullRenderers.Add(_signedUpPortalRenderers[i]);
        }

        for (int i = 0; i < _nullRenderers.Count; i++)
            _signedUpPortalRenderers.Remove(_nullRenderers[i]);
       
        // apply
        for (int r = 0; r < _signedUpPortalRenderers.Count; r++)
            SendPortalsToRenderer(_signedUpPortalRenderers[r]);
    }

    private void SendPortalsToRenderer(Renderer renderer)
    {
        var mat = renderer.material;

        mat.SetVectorArray("_Array", new Vector4[GameController.Instance.Portals.Length]);
        mat.SetInt("_ArrayLength", GameController.Instance.Portals.Length);

        Vector4[] portalSpots = new Vector4[GameController.Instance.Portals.Length];
        for (int i = 0; i < GameController.Instance.Portals.Length; i++)
        {
            if (GameController.Instance.Portals[i].Open)
                portalSpots[i] = GameController.Instance.Portals[i].transform.position - GameController.Instance.Portals[i].transform.forward;
            else
                portalSpots[i] = Vector3.up * 10000;
        }

        mat.SetVectorArray("_Array", portalSpots);
    }


    private void GameOver()
    {
        if( _gameOver == false)
        {
            StartCoroutine(GameOverRoutine());
            _gameOver = true;
        }
    }

    IEnumerator GameOverRoutine()
    {
        _player.Fired();

        yield return null;

        _canvasGroup.gameObject.SetActive(true);
        _canvasGroup.alpha = 0;

        _canvasGroup.DOFade(1, 4);
        yield return null;
    }
}
