using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core;
using Theriapolis.Core.World;
using Theriapolis.Core.World.Generation;

namespace Theriapolis.Game.Rendering;

/// <summary>
/// Renders the world map: biome tiles, rivers, roads, rail, and settlement icons.
/// Draw order: terrain → roads → rivers → rail → settlements.
/// </summary>
public sealed class WorldMapRenderer : IMapView, IDisposable
{
    private readonly WorldGenContext _ctx;
    private readonly TileAtlas _atlas;
    private readonly LineFeatureRenderer _lineRenderer;
    private bool _disposed;

    // Zoom level below which settlement labels are hidden to avoid clutter
    private const float LabelMinZoom = 0.5f;
    // Min tier to show at low zoom (hide tier 4 villages when zoomed out)
    private const float SettleHideZoom = 0.08f;

    public WorldMapRenderer(WorldGenContext ctx, TileAtlas atlas)
    {
        _ctx          = ctx;
        _atlas        = atlas;
        _lineRenderer = new LineFeatureRenderer(atlas.GraphicsDevice, ctx);
    }

    public void Draw(SpriteBatch sb, Camera2D camera, GameTime gameTime)
    {
        DrawTerrain(sb, camera);
        _lineRenderer.Draw(sb, camera);
        DrawSettlements(sb, camera);
    }

    private void DrawTerrain(SpriteBatch sb, Camera2D camera)
    {
        var (x0, y0, x1, y1) = camera.VisibleTileRect();
        int tilePixels = C.WORLD_TILE_PIXELS;

        sb.Begin(
            transformMatrix: camera.TransformMatrix,
            samplerState:    SamplerState.PointClamp,
            sortMode:        SpriteSortMode.Deferred);

        for (int ty = y0; ty <= y1; ty++)
        for (int tx = x0; tx <= x1; tx++)
        {
            ref var tile = ref _ctx.World.TileAt(tx, ty);
            var tex  = _atlas.GetTile(tile.Biome);
            var dest = new Rectangle(tx * tilePixels, ty * tilePixels, tilePixels, tilePixels);
            sb.Draw(tex, dest, Color.White);
        }

        sb.End();
    }

    private void DrawSettlements(SpriteBatch sb, Camera2D camera)
    {
        if (_ctx.World.Settlements.Count == 0) return;

        sb.Begin(
            transformMatrix: camera.TransformMatrix,
            samplerState:    SamplerState.LinearClamp,
            sortMode:        SpriteSortMode.Deferred,
            blendState:      BlendState.AlphaBlend);

        bool hideSmall = camera.Zoom < SettleHideZoom;

        foreach (var s in _ctx.World.Settlements)
        {
            if (hideSmall && s.Tier >= 4) continue;

            var icon   = _atlas.GetSettlementIcon(s.Tier);
            float wx   = s.TileX * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f;
            float wy   = s.TileY * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f;
            var origin = new Vector2(icon.Width * 0.5f, icon.Height * 0.5f);

            sb.Draw(icon,
                position:         new Vector2(wx, wy),
                sourceRectangle:  null,
                color:            Color.White,
                rotation:         0f,
                origin:           origin,
                scale:            1f,
                effects:          SpriteEffects.None,
                layerDepth:       0f);
        }

        sb.End();
    }

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;
        _lineRenderer.Dispose();
    }
}
