using NUnit.Framework;
using VMdemo.Core;
using VMdemo.Simulation;

namespace VMdemo.Tests.EditMode
{
    public class Step2AddressSplitTests
    {
        [TestCase(32)]
        [TestCase(48)]
        [TestCase(64)]
        public void AddressGenerator_NextVirtualAddress_StaysWithinRange(int vaBits)
        {
            var generator = new AddressGenerator(vaBits, seed: 7);

            for (var i = 0; i < 1000; i++)
            {
                var va = generator.NextVirtualAddress();
                Assert.That(va, Is.LessThanOrEqualTo(generator.MaxVirtualAddress));
            }
        }

        [Test]
        public void AddressGenerator_SameSeed_GeneratesDeterministicSequence()
        {
            var a = new AddressGenerator(48, seed: 99);
            var b = new AddressGenerator(48, seed: 99);

            for (var i = 0; i < 20; i++)
            {
                Assert.That(a.NextVirtualAddress(), Is.EqualTo(b.NextVirtualAddress()));
            }
        }

        [Test]
        public void AddressGenerator_DifferentSeed_GeneratesDifferentSequence()
        {
            var a = new AddressGenerator(48, seed: 99);
            var b = new AddressGenerator(48, seed: 100);
            var hasDifference = false;

            for (var i = 0; i < 20; i++)
            {
                if (a.NextVirtualAddress() != b.NextVirtualAddress())
                {
                    hasDifference = true;
                    break;
                }
            }

            Assert.IsTrue(hasDifference);
        }

        [TestCase(0x12345ABCUL, 32, 12, 0x12345UL, 0xABCUL, 0x48UL, 0x345UL)]
        [TestCase(0x0000ABCDEF123456UL, 48, 12, 0xABCDEF123UL, 0x456UL, 0x2AF37UL, 0x2F123UL)]
        [TestCase(0xFEDCBA9876543210UL, 64, 16, 0xFEDCBA987654UL, 0x3210UL, 0xFEDCBAUL, 0x987654UL)]
        public void SplitVirtualAddress_SupportedVaBits_ReturnsExpectedParts(
            ulong va,
            int vaBits,
            int offsetBits,
            ulong expectedVpn,
            ulong expectedOffset,
            ulong expectedL1,
            ulong expectedL2)
        {
            var parts = AddressTranslatorUtils.SplitVirtualAddress(va, vaBits, offsetBits);
            TestContext.WriteLine(AddressTranslatorUtils.FormatSplitResult(va, vaBits, offsetBits, parts));

            Assert.That(parts.Vpn, Is.EqualTo(expectedVpn));
            Assert.That(parts.Offset, Is.EqualTo(expectedOffset));
            Assert.That(parts.L1Index, Is.EqualTo(expectedL1));
            Assert.That(parts.L2Index, Is.EqualTo(expectedL2));
        }

        [Test]
        public void SplitVirtualAddress_OddVpnBits_UsesCeilFloorBitSplit()
        {
            const int vaBits = 32;
            const int offsetBits = 13;
            const ulong va = 0xDEADBEEFUL;

            var parts = AddressTranslatorUtils.SplitVirtualAddress(va, vaBits, offsetBits);
            AddressTranslatorUtils.GetPageTableBitLayout(vaBits - offsetBits, out var l1Bits, out var l2Bits);

            Assert.That(l1Bits, Is.EqualTo(10));
            Assert.That(l2Bits, Is.EqualTo(9));
            Assert.That(parts.Vpn, Is.EqualTo(0x6F56DUL));
            Assert.That(parts.Offset, Is.EqualTo(0x1EEFUL));
            Assert.That(parts.L1Index, Is.EqualTo(0x37AUL));
            Assert.That(parts.L2Index, Is.EqualTo(0x16DUL));
        }

        [Test]
        public void GetPageTableBitLayout_AlwaysSumsToVpnBits()
        {
            for (var vpnBits = 1; vpnBits <= 63; vpnBits++)
            {
                AddressTranslatorUtils.GetPageTableBitLayout(vpnBits, out var l1Bits, out var l2Bits);

                Assert.That(l1Bits + l2Bits, Is.EqualTo(vpnBits));
                Assert.That(l1Bits, Is.GreaterThanOrEqualTo(l2Bits));
                Assert.That(l1Bits - l2Bits, Is.LessThanOrEqualTo(1));
            }
        }

        [Test]
        public void SplitVirtualAddress_AddressOutOfRange_Throws()
        {
            Assert.Throws<System.ArgumentOutOfRangeException>(() =>
                AddressTranslatorUtils.SplitVirtualAddress(0x1_0000_0000UL, 32, 12));
        }
    }
}
