using Sunfish.Qr;
using Xunit;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;

namespace Sunfish.Tests;

/// <summary>El QR propio se comprueba leyendolo con un decodificador independiente (ZXing, solo en pruebas).</summary>
public class QrTests
{
    private static string? Decode(QrCode qr, int scale = 4, int quiet = 4)
    {
        int size = (qr.Size + quiet * 2) * scale;
        var pixels = new byte[size * size];
        Array.Fill(pixels, (byte)255);
        for (int y = 0; y < qr.Size; y++)
            for (int x = 0; x < qr.Size; x++)
                if (qr[x, y])
                    for (int dy = 0; dy < scale; dy++)
                        for (int dx = 0; dx < scale; dx++)
                            pixels[((y + quiet) * scale + dy) * size + (x + quiet) * scale + dx] = 0;
        var source = new RGBLuminanceSource(pixels, size, size, RGBLuminanceSource.BitmapFormat.Gray8);
        var hints = new Dictionary<DecodeHintType, object> { [DecodeHintType.PURE_BARCODE] = true, [DecodeHintType.CHARACTER_SET] = "UTF-8" };
        return new QRCodeReader().decode(new BinaryBitmap(new HybridBinarizer(source)), hints)?.Text;
    }

    [Theory]
    [InlineData("http://192.168.1.20:8080/")]
    [InlineData("http://[fe80::1234:5678:9abc:def0]:8080/fotos/")]
    [InlineData("ftp://192.168.1.20:2121/pub/")]
    [InlineData("Sunfish ñ é ü")]
    public void ShortTextsDecode(string text)
    {
        var qr = QrCode.Encode(text);
        Assert.Equal(text, Decode(qr));
    }

    [Theory]
    [InlineData(50)]
    [InlineData(150)]   // version 7+ (informacion de version)
    [InlineData(400)]   // varios bloques de longitudes distintas
    [InlineData(1200)]
    public void LongerTextsDecode(int length)
    {
        string text = string.Concat(Enumerable.Range(0, length).Select(i => (char)('a' + i * 7 % 26)));
        var qr = QrCode.Encode(text);
        Assert.Equal(text, Decode(qr));
    }

    [Fact]
    public void AllEccLevelsAndMasksDecode()
    {
        var masks = new HashSet<int>();
        foreach (var ecc in Enum.GetValues<QrEcc>())
            for (int i = 0; i < 40; i++)
            {
                string text = "http://10.0.0." + i + ":8080/" + new string('x', i);
                var qr = QrCode.Encode(text, ecc);
                Assert.True(qr.Ecc >= ecc);
                masks.Add(qr.Mask);
                Assert.Equal(text, Decode(qr));
            }
        Assert.True(masks.Count >= 4); // la eleccion de mascara no es siempre la misma
    }

    [Fact]
    public void UsesTheSmallestVersionAndRaisesEcc()
    {
        var qr = QrCode.Encode("http://192.168.1.20:8080/");
        Assert.Equal(2, qr.Version);
        Assert.Equal(25, qr.Size);
        Assert.True(qr.Ecc >= QrEcc.Medium);
    }

    [Fact]
    public void TooLongThrows() =>
        Assert.Throws<ArgumentException>(() => QrCode.Encode(new string('a', 3000)));
}
