using Friflo.Engine.ECS;
using Friflo.Engine.ECS.Systems;
using Microsoft.Xna.Framework;
using MrGameEng.Graphics;

namespace LittleSim.Net;

/// <summary>
/// Сглаживание сетевой позиции между снапшотами. Сервер шлёт ~10 снапшотов в секунду,
/// а рендер идёт на частоте кадра — без сглаживания жители телепортируются рывками.
/// Снапшот пишет в <see cref="Transform2D.Position"/>; система ловит это (позиция
/// разошлась с нарисованной), запоминает цель и каждый кадр экспоненциально подтягивает
/// видимую позицию к цели, записывая её обратно в трансформ для рендера.
/// </summary>
public struct NetLerp : IComponent
{
    /// <summary>Нарисованная (сглаженная) позиция прошлого кадра.</summary>
    public Vector2 Visual;

    /// <summary>Последняя серверная позиция — цель сглаживания.</summary>
    public Vector2 Target;

    /// <summary>Ложь до первого кадра: стартуем точно с серверной позиции, без подлёта.</summary>
    public bool Initialized;
}

/// <summary>
/// Двигает <see cref="NetLerp.Visual"/> к <see cref="NetLerp.Target"/> и пишет результат в
/// <see cref="Transform2D.Position"/>. Ставится после прокачки сети и до рендера.
/// </summary>
public sealed class NetSmoothingSystem : QuerySystem<Transform2D, NetLerp>
{
    // Скорость экспоненциального сглаживания: за ~0.25 с визуал почти догоняет цель.
    private const float Rate = 12f;

    protected override void OnUpdate()
    {
        var blend = 1f - MathF.Exp(-Rate * Tick.deltaTime);
        foreach (var (transforms, lerps, _) in Query.Chunks)
        {
            var t = transforms.Span;
            var l = lerps.Span;
            for (var i = 0; i < t.Length; i++)
            {
                ref var lerp = ref l[i];
                ref var position = ref t[i].Position;
                if (!lerp.Initialized)
                {
                    lerp.Visual = lerp.Target = position;
                    lerp.Initialized = true;
                    continue;
                }

                // Транформ трогает только снапшот: разошёлся с нарисованным — новая цель.
                if (position != lerp.Visual)
                {
                    lerp.Target = position;
                }

                lerp.Visual = Vector2.Lerp(lerp.Visual, lerp.Target, blend);
                position = lerp.Visual;
            }
        }
    }
}
