using Friflo.Engine.ECS;
using LittleSim.Content;
using LittleSim.Sim;
using Microsoft.Xna.Framework;
using MrGameEng.Core;
using MrGameEng.DevConsole;
using MrGameEng.Graphics;

namespace LittleSim.Scenes;

/// <summary>Спавн растительности по скаттеру дефов террейна.</summary>
internal static class ScatterSpawner
{
    /// <summary>
    /// Бросает каждую запись скаттера клетки независимо; при успехе ставит одно растение со
    /// случайным смещением и случайной (детерминированной сидом) зрелостью — мир после генерации
    /// разновозрастный. Стадия и спрайт берутся из <paramref name="plants"/>, дальше растением
    /// управляет <see cref="PlantGrowthSystem"/>.
    /// </summary>
    public static void Spawn(
        Scene scene,
        GameContent content,
        PlantSet plants,
        TerrainDef terrain,
        Point cell,
        int cellSize,
        Random random
    )
    {
        foreach (var entry in terrain.Scatter)
        {
            if (entry.Options.Count == 0 || random.NextSingle() >= entry.Chance)
            {
                continue;
            }

            var def = content.Defs.Get<PlantDef>(entry.Options[random.Next(entry.Options.Count)]);
            var index = plants.IndexOf(def);
            var px = (cell.X + 0.3f + random.NextSingle() * 0.4f) * cellSize;
            var py = (cell.Y + 0.3f + random.NextSingle() * 0.4f) * cellSize;
            var age = random.NextSingle() * plants[index].MaturityDays;

            PlantFactory.Create(
                scene.Store,
                plants,
                index,
                new Vector2(px, py),
                age,
                PlantGenome.FromDef(def.Genome, random),
                terrain.Fertility,
                cellSize
            );
        }
    }
}

/// <summary>Консольные команды контента: моды, язык, дефы, атласы (общие для сцен).</summary>
internal static class ContentCommands
{
    /// <summary>Регистрирует (или перерегистрирует) команды контента на общей консоли.</summary>
    public static void Register(DevConsole console, GameContent content, ModAtlases atlases)
    {
        console.Register(
            "mods",
            "mods — list active mods in load order",
            (c, _) =>
            {
                foreach (var mod in content.Mods)
                {
                    c.WriteLine($"  {mod.Id} {mod.Info.Version} — {mod.Info.Name}");
                }

                c.WriteLine($"{content.Mods.Count} mod(s)");
            }
        );

        console.Register(
            "lang",
            "lang [code] — show or switch the UI language",
            (c, args) =>
            {
                if (args.Length == 0)
                {
                    c.WriteLine(
                        $"language: {content.Languages.CurrentLanguage} "
                            + $"(available: {string.Join(", ", content.Languages.AvailableLanguages)})"
                    );
                    return;
                }

                c.WriteLine(
                    content.Languages.SetLanguage(args[0])
                        ? $"language set to {content.Languages.CurrentLanguage}"
                        : $"unknown language '{args[0]}'; available: {string.Join(", ", content.Languages.AvailableLanguages)}"
                );
            }
        );

        console.Register(
            "defs",
            "defs [type] — list def types or defs of one type",
            (c, args) =>
            {
                if (args.Length == 0)
                {
                    foreach (var typeKey in content.Defs.TypeKeys)
                    {
                        c.WriteLine($"  {typeKey}: {content.Defs.NamesOf(typeKey).Count} def(s)");
                    }

                    return;
                }

                var names = content.Defs.NamesOf(args[0]);
                if (names.Count == 0)
                {
                    c.WriteLine(
                        $"no defs of type '{args[0]}'; types: {string.Join(", ", content.Defs.TypeKeys)}"
                    );
                    return;
                }

                foreach (var name in names)
                {
                    c.WriteLine($"  {name}");
                }

                c.WriteLine($"{names.Count} def(s)");
            }
        );

        console.Register(
            "atlas",
            "atlas [name] [filter] — inspect loaded texture atlases",
            (c, args) =>
            {
                if (args.Length == 0)
                {
                    foreach (
                        var (name, atlas) in atlases.Loaded.OrderBy(
                            a => a.Key,
                            StringComparer.Ordinal
                        )
                    )
                    {
                        c.WriteLine(
                            $"  {name}: {atlas.Pages.Count} page(s), {atlas.Regions.Count} region(s)"
                        );
                    }

                    c.WriteLine($"{atlases.Loaded.Count} atlas(es) loaded");
                    return;
                }

                if (!atlases.Loaded.TryGetValue(args[0], out var found))
                {
                    c.WriteLine(
                        $"atlas '{args[0]}' is not loaded; loaded: {string.Join(", ", atlases.Loaded.Keys)}"
                    );
                    return;
                }

                var filter = args.Length > 1 ? args[1] : null;
                var keys = found
                    .Regions.Keys.Where(k =>
                        filter is null || k.Contains(filter, StringComparison.OrdinalIgnoreCase)
                    )
                    .Order(StringComparer.Ordinal)
                    .ToList();
                foreach (var key in keys.Take(25))
                {
                    c.WriteLine($"  {key}");
                }

                c.WriteLine(
                    $"{keys.Count} region(s){(keys.Count > 25 ? ", showing first 25" : "")}"
                );
            }
        );
    }
}
