using Microsoft.Xna.Framework.Graphics;
using MrGameEng.Graphics;

namespace LittleSim.Content;

/// <summary>
/// Готовая (resolved) таблица растений для сцены: по каждому <see cref="PlantDef"/> — массив
/// стадий с уже загруженными регионами атласа, размерами и порогами входа в днях. Строится один
/// раз при загрузке сцены (как <see cref="MrGameEng.Tilemaps.TileSet"/>, потому что нужны атлас и
/// устройство). <see cref="Sim.PlantGrowthSystem"/> индексируется в неё без словарей и аллокаций.
/// </summary>
public sealed class PlantSet
{
    /// <summary>Разрешённая стадия: спрайт, размер в клетках и день, с которого стадия активна.</summary>
    public readonly record struct Stage(Texture2DRegion Region, float SizeCells, float EnterDay);

    /// <summary>Вид растения: его деф и стадии роста по возрастанию <see cref="Stage.EnterDay"/>.</summary>
    public sealed class Species
    {
        /// <summary>Исходный деф растения.</summary>
        public required PlantDef Def { get; init; }

        /// <summary>Стадии роста по порядку; последняя — терминальная.</summary>
        public required Stage[] Stages { get; init; }

        /// <summary>День, с которого растение достигает последней (зрелой) стадии.</summary>
        public float MaturityDays => Stages[^1].EnterDay;

        /// <summary>Индекс стадии для данного возраста в днях.</summary>
        public int StageAt(float ageDays)
        {
            var stage = 0;
            while (stage + 1 < Stages.Length && ageDays >= Stages[stage + 1].EnterDay)
            {
                stage++;
            }

            return stage;
        }
    }

    private readonly Species[] _species;
    private readonly Dictionary<PlantDef, int> _index = new();

    /// <summary>Строит таблицу из всех (не-abstract) <see cref="PlantDef"/> мода.</summary>
    public PlantSet(GameContent content, ModAtlases atlases, GraphicsDevice device)
    {
        var defs = content.Defs.All<PlantDef>().ToArray();
        _species = new Species[defs.Length];
        for (var i = 0; i < defs.Length; i++)
        {
            var def = defs[i];
            _species[i] = new Species { Def = def, Stages = BuildStages(def, atlases, device) };
            _index[def] = i;
        }
    }

    /// <summary>Число видов в таблице.</summary>
    public int Count => _species.Length;

    /// <summary>Вид по индексу.</summary>
    public Species this[int index] => _species[index];

    /// <summary>Индекс вида по дефу.</summary>
    public int IndexOf(PlantDef def) => _index[def];

    private static Stage[] BuildStages(PlantDef def, ModAtlases atlases, GraphicsDevice device)
    {
        // Без явных стадий растение существует как одна терминальная стадия из базовой текстуры.
        if (def.Stages.Count == 0)
        {
            var region = atlases.GetRegion(device, def.Texture);
            return [new Stage(region, def.SizeCells, 0f)];
        }

        var stages = new Stage[def.Stages.Count];
        var enterDay = 0f;
        for (var i = 0; i < def.Stages.Count; i++)
        {
            var stage = def.Stages[i];
            var region = atlases.GetRegion(device, stage.Texture ?? def.Texture);
            var size = stage.SizeCells > 0f ? stage.SizeCells : def.SizeCells;
            stages[i] = new Stage(region, size, enterDay);
            enterDay += stage.GrowDays; // следующая стадия входит после GrowDays этой
        }

        return stages;
    }
}
