using System;

namespace Mr.BrickAdventures.Tools.CaLevelGenerator;

public static class CaGenerator
{
    public static bool[,] Generate(CaGeneratorSettings s)
    {
        var rng = new Random(s.Seed);
        return s.Mode switch
        {
            CaMode.Cave     => GenerateCave(s, rng),
            CaMode.Platform => GeneratePlatform(s, rng),
            CaMode.Terrain  => GenerateTerrain(s, rng),
            _               => GenerateCave(s, rng),
        };
    }

    public static bool[,] Smooth(bool[,] grid) => SmoothPass(grid, 5);

    // ── Cave ──────────────────────────────────────────────────────────────
    private static bool[,] GenerateCave(CaGeneratorSettings s, Random rng)
    {
        var grid = RandomFill(s.Width, s.Height, s.FillDensity, rng);
        if (s.BorderWalls) EnforceBorder(grid);
        for (int i = 0; i < s.SmoothingPasses; i++)
        {
            grid = SmoothPass(grid, 5);
            if (s.BorderWalls) EnforceBorder(grid);
        }
        return grid;
    }

    // ── Platform ──────────────────────────────────────────────────────────
    private static bool[,] GeneratePlatform(CaGeneratorSettings s, Random rng)
    {
        int w = s.Width, h = s.Height;
        var grid = new bool[w, h];

        for (int x = 0; x < w; x++)
        {
            grid[x, h - 1] = true;
            grid[x, h - 2] = true;

            for (int y = 0; y < h - 2; y++)
            {
                // Density linearly increases from 10% at top to 60% at bottom,
                // scaled by user's FillDensity relative to default 0.48.
                float t = (float)y / (h - 3);
                float rowDensity = (0.1f + t * 0.5f) * (s.FillDensity / 0.48f);
                grid[x, y] = rng.NextDouble() < rowDensity;
            }
        }

        for (int i = 0; i < s.SmoothingPasses; i++)
        {
            // Threshold 4 (not 5): horizontal platform groups survive, isolated
            // vertical pixels collapse into flat platforms.
            grid = SmoothPass(grid, 4);
            for (int x = 0; x < w; x++) { grid[x, h - 1] = true; grid[x, h - 2] = true; }
        }

        return grid;
    }

    // ── Terrain ───────────────────────────────────────────────────────────
    private static bool[,] GenerateTerrain(CaGeneratorSettings s, Random rng)
    {
        int w = s.Width, h = s.Height;
        var grid = new bool[w, h];

        // Start terrain surface at ~60% from bottom (40% from top)
        int surfaceRow = (int)(h * 0.4f);

        for (int x = 0; x < w; x++)
        {
            int delta = rng.Next(-2, 3);
            surfaceRow = Math.Clamp(surfaceRow + delta, h / 6, h - 2);

            for (int y = surfaceRow; y < h; y++)
                grid[x, y] = true;
        }

        for (int i = 0; i < s.SmoothingPasses; i++)
            grid = SmoothPass(grid, 5);

        return grid;
    }

    // ── Shared helpers ────────────────────────────────────────────────
    private static bool[,] RandomFill(int w, int h, float density, Random rng)
    {
        var grid = new bool[w, h];
        for (int x = 0; x < w; x++)
            for (int y = 0; y < h; y++)
                grid[x, y] = rng.NextDouble() < density;
        return grid;
    }

    private static bool[,] SmoothPass(bool[,] grid, int threshold)
    {
        int w = grid.GetLength(0), h = grid.GetLength(1);
        var next = new bool[w, h];
        for (int x = 0; x < w; x++)
            for (int y = 0; y < h; y++)
                next[x, y] = CountSolidNeighbors(grid, x, y) >= threshold;
        return next;
    }

    private static int CountSolidNeighbors(bool[,] grid, int cx, int cy)
    {
        int w = grid.GetLength(0), h = grid.GetLength(1), count = 0;
        for (int dx = -1; dx <= 1; dx++)
            for (int dy = -1; dy <= 1; dy++)
            {
                if (dx == 0 && dy == 0) continue;
                int nx = cx + dx, ny = cy + dy;
                // Out-of-bounds counts as solid (closes cave edges naturally)
                if (nx < 0 || nx >= w || ny < 0 || ny >= h) count++;
                else if (grid[nx, ny]) count++;
            }
        return count;
    }

    private static void EnforceBorder(bool[,] grid)
    {
        int w = grid.GetLength(0), h = grid.GetLength(1);
        for (int x = 0; x < w; x++) { grid[x, 0] = true; grid[x, h - 1] = true; }
        for (int y = 0; y < h; y++) { grid[0, y] = true; grid[w - 1, y] = true; }
    }
}
