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

public class SheepGenerator : MonoBehaviour
{
    [SerializeField]
    private LevelManager _levelManager;

    [SerializeField]
    private Sheep _sheep;
    private List<Sheep> _sheeps = new List<Sheep>();

    [SerializeField]
    private StageData[] _stageData;

    [SerializeField]
    private Vector2 mapSize;

    [SerializeField]
    private float _minRandomDistance = 1f;

    [SerializeField]
    private float mapDetail;

    [SerializeField]
    private Board _board = null;

    [SerializeField]
    private RestartBell _restartBell = null;

    [SerializeField]
    private ApplicationChannel _applicationChannel = null;

    [SerializeField]
    private Transform _gameplayContainer = null;

    [SerializeField] private TextMeshPro _sheepTotalValueLabel = null;

    private int stageCount = 0;
    private int targetValue = 0;
    private int sheepCount = 0;

    private int sheepTotalvalue{
        get {
            int value = 0;
            for (int i = 0; i < sheepCount; i++)
                value += (int)sheeps[i].value;
            return value;
        }
    }

    private Sheep[] sheeps;
    private int[] shuffleArray;
    private int divideX,
        divideY;

    private void Start()
    {
        divideX = Mathf.RoundToInt(mapSize.x / mapDetail);
        divideY = Mathf.RoundToInt(mapSize.y / mapDetail);
        shuffleArray = new int[divideX * divideY];
        for (int i = 0; i < shuffleArray.Length; i++)
            shuffleArray[i] = i;

        int maxSheepCount = 0;
        for (int i = 0; i < _stageData.Length; i++)
            if (maxSheepCount < _stageData[i].sheepStartValue.Length)
                maxSheepCount = _stageData[i].sheepStartValue.Length;
        sheeps = new Sheep[maxSheepCount];

        InstantiateSheep();
    }

    private void Shuffle()
    {
        for (int i = 0; i < shuffleArray.Length; i++)
        {
            int temp = shuffleArray[i];
            int randomIndex = Random.Range(0, shuffleArray.Length);
            shuffleArray[i] = shuffleArray[randomIndex];
            shuffleArray[randomIndex] = temp;
        }
    }

    [ContextMenu("Generate")]
    public void SheepGenerate()
    {
        if (stageCount == _stageData.Length - 1)
        {
            _applicationChannel.onClearedGame.Invoke();
        }
        else
        {
            stageCount++;

            Shuffle();
            for (int i = 0; i < sheepCount; i++)
                Destroy(sheeps[i].gameObject);
            for (int i = 0; i < sheeps.Length; i++)
                sheeps[i] = null;

            InstantiateSheep();
        }
    }

    private void InstantiateSheep()
    {
        _sheeps.Clear();

        sheepCount = _stageData[stageCount].sheepStartValue.Length;
        List<Vector3> randomPositions = GetRandomPositionsInPen(sheepCount);

        for (int i = 0; i < sheepCount; i++)
        {
            var sheep = Instantiate(
                _sheep,
                randomPositions[i],
                Quaternion.Euler(0, Random.Range(-180f, 180f), 0),
                _gameplayContainer
            );
            sheep.value = _stageData[stageCount].sheepStartValue[i];
            sheeps[i] = sheep;
            _sheeps.Add(sheep);
        }
        targetValue = _stageData[stageCount].targetValue;

        _board.ChangeText(targetValue.ToString());
        RefreshSheepTotalValueLabel();

    }

    /// <summary>
    /// Calculate the total number of sheep and determine if they go to the next level.
    /// </summary>
    public void CheckTotalValue()
    {
        RefreshSheepTotalValueLabel();

        if (sheepTotalvalue == targetValue)
            StartCoroutine("DelayOnCompletedStage");
        else if (sheepTotalvalue < targetValue)
            _restartBell.ChangeState(RestartBellState.Ringing);
    }

    public void RefreshSheepTotalValueLabel()
    {
        _sheepTotalValueLabel.text = sheepTotalvalue.ToString();
    }



    public IEnumerator DelayOnCompletedStage()
    {
        foreach (Sheep sheep in _sheeps)
            sheep.MakeHappy();
        yield return new WaitForSeconds(2f);
        SheepGenerate();
    }

    [ContextMenu("Reset")]
    public void Restart()
    {
        Shuffle();
        for (int i = 0; i < sheepCount; i++)
            Destroy(sheeps[i].gameObject);
        for (int i = 0; i < sheeps.Length; i++)
            sheeps[i] = null;

        InstantiateSheep();

        StartCoroutine("SetRestartBellIdle");
    }

    private IEnumerator SetRestartBellIdle()
    {
        yield return new WaitForSeconds(0.5f);
        _restartBell.ChangeState(RestartBellState.Idle);
    }

    #region Random Position

    public Vector3 GetRandomPositionInPen()
    {
        return new Vector3(
            Random.Range(-mapSize.x / 2f, mapSize.x / 2f),
            0f,
            Random.Range(-mapSize.y / 2f, mapSize.y / 2f)
        );
    }

    public List<Vector3> GetRandomPositionsInPen(int amount)
    {
        List<Vector3> positions = new List<Vector3>();
        Vector3 position;

        for (int i = 0; i < amount; i++)
        {
            if (i == 0)
                positions.Add(GetRandomPositionInPen());
            else
            {
                REPEAT:
                position = GetRandomPositionInPen();
                foreach (Vector3 storedPosition in positions)
                {
                    if (Vector3.Distance(position, storedPosition) < _minRandomDistance)
                        goto REPEAT;
                }
                positions.Add(position);
            }
        }

        return positions;
    }

    [ContextMenu("TEST RANDOM POSITIONS")]
    public void TestRandomPosition()
    {
        List<Vector3> randomPositions = GetRandomPositionsInPen(6);

        for (int i = 0; i < randomPositions.Count; i++)
        {
            GameObject test = new GameObject();
            test.transform.SetParent(_gameplayContainer);
            test.transform.position = randomPositions[i];
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(Vector3.zero, new Vector3(mapSize.x, 1f, mapSize.y));
    }

    #endregion
}
