using FluentAssertions;
using DragonISO.Engine.Pipeline;

namespace DragonISO.Engine.Tests.Pipeline;

public class TestPatternGeneratorTests
{
    [Theory]
    [InlineData(1280, 720)]
    [InlineData(1920, 1080)]
    [InlineData(640, 480)]
    public void Render_ProducesBgraBufferOfExpectedSize(int width, int height)
    {
        var frame = TestPatternGenerator.Render(width, height, frameNumber: 0, timestampTicks: 0);

        frame.Width.Should().Be(width);
        frame.Height.Should().Be(height);
        frame.Pixels.Length.Should().Be(width * height * 4);
        frame.Format.Should().Be(PixelFormat.Bgra);
    }

    [Fact]
    public void Render_AlphaChannelIsFullyOpaque()
    {
        var frame = TestPatternGenerator.Render(640, 480, frameNumber: 0, timestampTicks: 0);

        // Spot-check alpha = 0xFF at four corners.
        var px = frame.Pixels.Span;
        px[3].Should().Be(0xFF);
        px[(640 - 1) * 4 + 3].Should().Be(0xFF);
        px[(479 * 640) * 4 + 3].Should().Be(0xFF);
        px[(479 * 640 + 639) * 4 + 3].Should().Be(0xFF);
    }

    [Fact]
    public void Render_BarsAreColorful()
    {
        // The eight color bars should produce eight distinct (R,G,B) triplets
        // when sampled at the middle of each bar's column.
        var frame = TestPatternGenerator.Render(800, 100, frameNumber: 0, timestampTicks: 0);
        var px = frame.Pixels.Span;
        var midRow = 50;
        var barWidth = 800 / 8;

        var seen = new HashSet<(byte, byte, byte)>();
        for (var bar = 0; bar < 8; bar++)
        {
            var x = bar * barWidth + barWidth / 2;
            var off = (midRow * 800 + x) * 4;
            seen.Add((px[off], px[off + 1], px[off + 2]));
        }
        seen.Count.Should().Be(8, because: "the 8 SMPTE bars must be visually distinguishable");
    }

    [Fact]
    public void Render_DifferentFrameNumbers_ProduceDifferentSweepRows()
    {
        // The sweep band moves down by 2 rows per frame. Two frames separated by
        // 100 should differ at the sweep rows.
        var frame0 = TestPatternGenerator.Render(640, 480, frameNumber: 0, timestampTicks: 0);
        var frame100 = TestPatternGenerator.Render(640, 480, frameNumber: 100, timestampTicks: 0);

        // frame0's sweep row is 0 (and Â±4 rows). frame100's sweep row is 200.
        // Compare pixel row 200 â€” frame100 should be brighter at that row.
        var px0 = frame0.Pixels.Span;
        var px100 = frame100.Pixels.Span;
        var row = 200;
        var samples = 0;
        for (var x = 0; x < 640; x++)
        {
            var off = (row * 640 + x) * 4;
            // Most non-black bars: brighter sample at frame100 means the sweep is there.
            var sum0 = px0[off] + px0[off + 1] + px0[off + 2];
            var sum100 = px100[off] + px100[off + 1] + px100[off + 2];
            if (sum100 > sum0) samples++;
        }
        // Allow some bars to be the black bar (where +32 still tops out at 32);
        // most bars should brighten with the sweep.
        samples.Should().BeGreaterThan(640 / 2,
            because: "the sweep band brightens most pixels at row 200 in frame 100");
    }
}
