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

[ExecuteAlways]
public class Lorax : MonoBehaviour {

    public Terrain Terrain;

    [Header("Generic Gen")]
    public int Seed;
    public bool Regen = false;
    public bool ShowInEditor = true;
    public List<GameObject> SpawnableTrees;
    public List<int> Chances;
    public bool DisableOnStart = true;

    [Header("Generation Details")]
    public float HeightMin = 7.5f;
    public float HeightMax = 9;
    public float TreeChance = 0.9f;
    public float CampSize = 10f;
    public Vector3 CampMiddle;
    public float RandomNudge = 2f;
    public float Denseness = 4;
    public float MinimumAdjacency = 1.5f;

    private int LastSeed = -1;
    private Vector3[][] TreePositions;
    private bool Generated = false;

    private int ChunkSize = 20;
    private GameObject[] Chunks;

    void Start() {
        Generate();
    }

    void Update() {
        if (!Application.isPlaying) {
            if (!ShowInEditor) {
                if (Generated) {
                    DestroyGenerated();
                }
                return;
            }
            if (Generated && LastSeed == Seed && !Regen) {
                return;
            }
            Regen = false;
            LastSeed = Seed;
            DestroyGenerated();
            Generate();
        }
    }

    public GameObject GetChunkAt(float x, float y) {
        int CurrentChunk = (int)((Mathf.Floor((y + 250f) / ChunkSize) * 25f) + Mathf.Floor((x + 250f) / ChunkSize));
        return Chunks[CurrentChunk];
    }

    public void DestroyGenerated() {
        for (int x = transform.childCount - 1; x >= 0; x--) {
            DestroyImmediate(transform.GetChild(0).gameObject);
        }
        Chunks = new GameObject[(500 / ChunkSize) * (500 / ChunkSize)];
        Generated = false;
    }

    public void Generate() {
        Random.InitState(Seed);
        Generated = true;
        Chunks = new GameObject[(500 / ChunkSize) * (500 / ChunkSize)];
        int[] TotalChances = new int[SpawnableTrees.Count + 1];
        int Counter = 0;
        for (int x = 0; x < TotalChances.Length - 1; x++) {
            TotalChances[x + 1] = Counter + Chances[x];
            Counter = TotalChances[x + 1];
        }
        int cap = (int)Mathf.Floor(500 / Denseness);
        TreePositions = new Vector3[cap][];
        for (int y = 0; y < cap; y++) {
            TreePositions[y] = new Vector3[cap];
            for (int x = 0; x < cap; x++) {
                int CurrentChunk = (int)((Mathf.Floor(y * Denseness / ChunkSize) * 25) + Mathf.Floor(x * Denseness / ChunkSize));
                if (Chunks[CurrentChunk] == null) {
                    Chunks[CurrentChunk] = new GameObject("Chunk " + CurrentChunk);
                    Chunks[CurrentChunk].transform.parent = transform;
                    if (Application.isPlaying) {
                        Chunks[CurrentChunk].SetActive(!DisableOnStart);
                    }
                }
                if (Random.value > TreeChance) {
                    continue;
                }
                var pos = new Vector3(x * Denseness - 250 + Random.value * RandomNudge - RandomNudge / 2, 0, y * Denseness - 250 + Random.value * RandomNudge - RandomNudge / 2);
                TreePositions[y][x] = pos;
                if (x > 0 && (TreePositions[y][x - 1] - pos).magnitude < MinimumAdjacency) {
                    continue;
                }
                if (y > 0 && (TreePositions[y - 1][x] - pos).magnitude < MinimumAdjacency) {
                    continue;
                }
                if ((pos - CampMiddle).magnitude < CampSize) {
                    continue;
                }

                var height = Terrain.SampleHeight(pos);
                if (height >= HeightMin && height <= HeightMax) {
                    pos.y = Terrain.transform.position.y + height;
                    var rand = Random.Range(0, TotalChances[TotalChances.Length - 1]);
                    var Chosen = SpawnableTrees[0];
                    for (int curr = TotalChances.Length - 2; curr > 0; curr--) {
                        if (rand > TotalChances[curr]) {
                            Chosen = SpawnableTrees[curr];
                            break;
                        }
                    }
                    var rot = Quaternion.Euler(0, 360 * Random.value, 0);
                    var obj = GameObject.Instantiate(Chosen, pos, rot, Chunks[CurrentChunk].transform);
                }
            }
        }
    }
}
