using Theriapolis.Core.Util;

namespace Theriapolis.Core.World.Generation.Stages;

/// <summary>
/// Stage 5 — TemperatureGen
/// Temperature is primarily latitude-derived (north = cold, south = warm),
/// modified by elevation (higher = colder) and minor noise for local variation.
/// Respects macro-cell temperature modifiers.
/// </summary>
public sealed class TemperatureGenStage : IWorldGenStage
{
    public string Name => "TemperatureGen";

    public void Run(WorldGenContext ctx)
    {
        ulong seed = ctx.World.WorldSeed ^ C.RNG_TEMP;
        var noise = new FastNoiseLite
        {
            Seed       = (int)(seed & 0x7FFFFFFF),
            Frequency  = 1.5f / C.WORLD_WIDTH_TILES,
            Noise      = FastNoiseLite.NoiseType.OpenSimplex2,
            Fractal    = FastNoiseLite.FractalType.FBm,
            Octaves    = 3,
            Lacunarity = 2.0f,
            Gain       = 0.5f,
        };

        int W = C.WORLD_WIDTH_TILES;
        int H = C.WORLD_HEIGHT_TILES;

        // FastNoiseLite.EnsurePerm() is not thread-safe; calling GetNoise once here
        // populates _perm in the main thread before the parallel loop reads it.
        _ = noise.GetNoise(0f, 0f);

        Parallel.For(0, H, ty =>
        {
            // Latitude: 0 at north (cold), 1 at south (warm)
            float latitude = (float)ty / (H - 1);

            for (int tx = 0; tx < W; tx++)
            {
                float elev = ctx.World.Tiles[tx, ty].Elevation;

                // Base temperature from latitude (smooth curve — coldest at top)
                float baseTemp = latitude * latitude * 0.8f + latitude * 0.2f;

                // Elevation cooling: above sea level, temperature drops ~0.3 per unit elevation
                float elevCool = MathF.Max(0f, elev - WorldState.SeaLevel) * 0.45f;

                // Minor local noise
                float localNoise = noise.GetNoise((float)tx, (float)ty) * 0.07f;

                float t = baseTemp - elevCool + localNoise;

                // Macro cell modifier — use the warped cell stored on the
                // tile so temperature inherits the same organic cell shape.
                var cell = ctx.World.MacroCellForTile(ctx.World.Tiles[tx, ty]);
                t += cell.TempModifier;

                ctx.World.Tiles[tx, ty].Temperature = Math.Clamp(t, 0f, 1f);
            }
        });

        ctx.World.StageHashes["TemperatureGen"] = ctx.World.HashTemperature();
        ctx.LogMessage($"[TemperatureGen] Temperature hash: 0x{ctx.World.StageHashes["TemperatureGen"]:X16}");
    }
}
