namespace Theriapolis.Core.World.Generation;

/// <summary>
/// Computes 8-connected land-component IDs and sizes via flood fill. Settlement
/// placement uses this to confine settlements to the main landmass — roads
/// can't cross ocean, so a settlement on a disconnected island would either be
/// unreachable or get a sea-crossing straight-line connector stub from
/// <c>EnsureSettlementConnectivity</c>. 8-connected matches A*'s movement
/// model so "reachable by road" and "same component" coincide.
/// </summary>
internal static class LandmassMap
{
    private const int W = C.WORLD_WIDTH_TILES;
    private const int H = C.WORLD_HEIGHT_TILES;

    private static readonly (int dx, int dy)[] Neighbors =
    {
        ( 0,-1), ( 1,-1), ( 1, 0), ( 1, 1),
        ( 0, 1), (-1, 1), (-1, 0), (-1,-1),
    };

    /// <summary>
    /// Returns a pair of arrays: <c>componentId[x,y]</c> gives the 8-connected
    /// land component ID at (x,y), or -1 if (x,y) is ocean; <c>componentSize[id]</c>
    /// gives the tile count of component <paramref name="id"/>.
    /// </summary>
    public static (int[,] componentId, int[] componentSize) Compute(WorldState world)
    {
        var compId = new int[W, H];
        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++)
            compId[x, y] = -1;

        var sizes = new List<int>();
        var queue = new Queue<(int x, int y)>();

        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++)
        {
            if (compId[x, y] != -1) continue;
            if (world.Tiles[x, y].Biome == BiomeId.Ocean) continue;

            int id = sizes.Count;
            int size = 0;
            compId[x, y] = id;
            queue.Enqueue((x, y));

            while (queue.Count > 0)
            {
                var (cx, cy) = queue.Dequeue();
                size++;
                foreach (var (dx, dy) in Neighbors)
                {
                    int nx = cx + dx, ny = cy + dy;
                    if ((uint)nx >= W || (uint)ny >= H) continue;
                    if (compId[nx, ny] != -1) continue;
                    if (world.Tiles[nx, ny].Biome == BiomeId.Ocean) continue;
                    compId[nx, ny] = id;
                    queue.Enqueue((nx, ny));
                }
            }

            sizes.Add(size);
        }

        return (compId, sizes.ToArray());
    }

    /// <summary>
    /// Returns the component ID of the largest land component, or -1 if the
    /// world has no land.
    /// </summary>
    public static int LargestComponentId(int[] componentSizes)
    {
        int best = -1;
        int bestSize = 0;
        for (int i = 0; i < componentSizes.Length; i++)
            if (componentSizes[i] > bestSize) { bestSize = componentSizes[i]; best = i; }
        return best;
    }
}
