using System;

namespace VMdemo.Core
{
    public static class AddressTranslatorUtils
    {
        public static AddressParts SplitVirtualAddress(ulong virtualAddress, int vaBits, int offsetBits)
        {
            if (vaBits <= 0 || vaBits > 64)
            {
                throw new ArgumentOutOfRangeException(nameof(vaBits), "vaBits 必须在 1 到 64 之间。");
            }

            if (offsetBits < 0 || offsetBits >= vaBits)
            {
                throw new ArgumentOutOfRangeException(nameof(offsetBits), "offsetBits 必须满足 0 <= offsetBits < vaBits。");
            }

            var vaMask = GetBitMask(vaBits);
            if (vaBits < 64 && (virtualAddress & ~vaMask) != 0UL)
            {
                throw new ArgumentOutOfRangeException(nameof(virtualAddress), "virtualAddress 超出 vaBits 可表示范围。");
            }

            var vpnBits = vaBits - offsetBits;
            GetPageTableBitLayout(vpnBits, out _, out var l2Bits);

            var offset = virtualAddress & GetBitMask(offsetBits);
            var vpn = virtualAddress >> offsetBits;
            var l2Index = vpn & GetBitMask(l2Bits);
            var l1Index = vpn >> l2Bits;

            return new AddressParts(vpn, offset, l1Index, l2Index);
        }

        public static void GetPageTableBitLayout(int vpnBits, out int l1Bits, out int l2Bits)
        {
            if (vpnBits <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(vpnBits), "vpnBits 必须是正整数。");
            }

            l1Bits = (vpnBits + 1) / 2;
            l2Bits = vpnBits / 2;
        }

        public static string FormatSplitResult(
            ulong virtualAddress,
            int vaBits,
            int offsetBits,
            AddressParts parts)
        {
            return $"VA={virtualAddress}, VPN={parts.Vpn}, Offset={parts.Offset}, L1={parts.L1Index}, L2={parts.L2Index}, vaBits={vaBits}, offsetBits={offsetBits}";
        }

        private static ulong GetBitMask(int bits)
        {
            if (bits <= 0)
            {
                return 0UL;
            }

            if (bits >= 64)
            {
                return ulong.MaxValue;
            }

            return (1UL << bits) - 1UL;
        }
    }
}
