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

public class TerrainGenerator : Manager<TerrainGenerator>
{
    public int ChunkRadiusX = 1;
    public int ChunkRadiusY = 10;
    public GameObject ChunkPrefab;
    public GameObject TreePrefab;
    public GameObject SnowmanObstaclePrefab;
    
    public GameObject PickupPrefab;

    public AnimationCurve TrenchProfile;

    private Dictionary<Vector2Int, Chunk> chunks = new Dictionary<Vector2Int, Chunk>();

    private const int LOAD_PER_UPDATE = 1;

    public void FixedUpdate()
    {
        HashSet<Vector2Int> loadedPositions = new HashSet<Vector2Int>();

        for(int x=-ChunkRadiusX;x<=ChunkRadiusX;x++)
        {
            for(int y=-ChunkRadiusY;y<=ChunkRadiusY;y++)
            {
                Vector2Int localChunkPos = new Vector2Int(x, y);
                Vector3 playerGlobalPos = Player.Instance.transform.position;
                Vector2Int playerPos = new Vector2Int((int)(playerGlobalPos.x / Chunk.CHUNK_SIZE), (int)(playerGlobalPos.z / Chunk.CHUNK_SIZE));

                Vector2Int globalChunkPos = playerPos + localChunkPos;
                loadedPositions.Add(globalChunkPos);
            }
        }

        List<KeyValuePair<Vector2Int, Chunk>> unload = chunks.Where(x => !loadedPositions.Contains(x.Key)).ToList();

        foreach(KeyValuePair<Vector2Int, Chunk> instance in unload)
        {
            PoolManager.Instance.ReturnInstance(instance.Value.gameObject);
            chunks.Remove(instance.Key);
        }

        List<Vector2Int> load = loadedPositions.Where(x => !chunks.ContainsKey(x)).ToList();
        if(GameStateManager.Instance.GameState == GameState.Play)
            load = load.Take(LOAD_PER_UPDATE).ToList();
        foreach(Vector2Int pos in load)
        {
            Chunk c = PoolManager.Instance.GetInstance(ChunkPrefab).GetComponent<Chunk>();
            c.UpdatePosition(Vector3.Scale(Vector3.one * Chunk.CHUNK_SIZE, new Vector3(pos.x, 0.0f, pos.y)));
            chunks[pos] = c;
        }
    }

    public const float noise_seed = 10000.0f;

    public float GetPathCenter(float y)
    {
        float pathwayNoiseScale = 0.025f;
        float pathwayBlockingNoiseLayer = (Mathf.PerlinNoise(0.5f, noise_seed + y * pathwayNoiseScale) * 2.0f) - 1.0f;

        return pathwayBlockingNoiseLayer;
    }

    public float GetDistanceFromPath(float x, float y)
    {
        
        float distance = ((x/15.0f) - GetPathCenter(y));

        return distance;
    }

    public float GetHeight(float x, float y)
    {
        float x_pow_2 = x * x;
        float x_pow_3 = x * x * x;
        float x_pow_4 = x * x * x * x;

        float baseHeight = 0.000001f * x_pow_4;

        float noise_scale_0 = 0.05f;
        float noise_0 = Mathf.PerlinNoise(noise_seed + x * noise_scale_0, noise_seed + y * noise_scale_0) * 2.0f - 1.0f;

        float noise_scale_1 = 0.001f;
        float noise_1 = Mathf.PerlinNoise(noise_seed + x * noise_scale_1, noise_seed + y * noise_scale_1) * 2.0f - 1.0f;

        float noise_scale_2 = 0.001f;
        float noise_2 = Mathf.PerlinNoise(noise_seed + x * noise_scale_2, noise_seed + y * noise_scale_2) * 2.0f - 1.0f;

        float distance = GetDistanceFromPath(x, y);

        float height = baseHeight + (noise_0 * 5.0f) * TrenchProfile.Evaluate(distance) + noise_1 * 5.0f + noise_2 * 10.0f;

        return height + ((TrenchProfile.Evaluate(distance)-1.0f)*3.0f);
    }
}
