using System;
using System.Runtime.InteropServices;
using CpuAddress = System.UInt64;
using DspAddress = System.UInt64;

namespace Ryujinx.Audio.Renderer.Server.MemoryPool
{
    /// <summary>
    /// Server state for a memory pool.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)]
    public struct MemoryPoolState
    {
        public const int Alignment = 0x10;

        /// <summary>
        /// The location of the <see cref="MemoryPoolState"/>.
        /// </summary>
        public enum LocationType : uint
        {
            /// <summary>
            /// <see cref="MemoryPoolState"/> located on the CPU side for user use.
            /// </summary>
            Cpu,

            /// <summary>
            /// <see cref="MemoryPoolState"/> located on the DSP side for system use.
            /// </summary>
            Dsp
        }

        /// <summary>
        /// The CPU address associated to the <see cref="MemoryPoolState"/>.
        /// </summary>
        public CpuAddress CpuAddress;

        /// <summary>
        /// The DSP address associated to the <see cref="MemoryPoolState"/>.
        /// </summary>
        public DspAddress DspAddress;

        /// <summary>
        /// The size associated to the <see cref="MemoryPoolState"/>.
        /// </summary>
        public ulong Size;

        /// <summary>
        /// The <see cref="LocationType"/> associated to the <see cref="MemoryPoolState"/>.
        /// </summary>
        public LocationType Location;

        /// <summary>
        /// Set to true if the <see cref="MemoryPoolState"/> is used.
        /// </summary>
        [MarshalAs(UnmanagedType.I1)]
        public bool IsUsed;

        public static unsafe MemoryPoolState* Null => (MemoryPoolState*)IntPtr.Zero.ToPointer();

        /// <summary>
        /// Create a new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>.
        /// </summary>
        /// <param name="location">The location type to use.</param>
        /// <returns>A new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>.</returns>
        public static MemoryPoolState Create(LocationType location)
        {
            return new MemoryPoolState
            {
                CpuAddress = 0,
                DspAddress = 0,
                Size = 0,
                Location = location
            };
        }

        /// <summary>
        /// Set the <see cref="CpuAddress"/> and size of the <see cref="MemoryPoolState"/>.
        /// </summary>
        /// <param name="cpuAddress">The <see cref="CpuAddress"/>.</param>
        /// <param name="size">The size.</param>
        public void SetCpuAddress(CpuAddress cpuAddress, ulong size)
        {
            CpuAddress = cpuAddress;
            Size = size;
        }

        /// <summary>
        /// Check if the given <see cref="CpuAddress"/> and size is contains in the <see cref="MemoryPoolState"/>.
        /// </summary>
        /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param>
        /// <param name="size">The size.</param>
        /// <returns>True if the <see cref="CpuAddress"/> is contained inside the <see cref="MemoryPoolState"/>.</returns>
        public bool Contains(CpuAddress targetCpuAddress, ulong size)
        {
            if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Translate the given CPU address to a DSP address.
        /// </summary>
        /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param>
        /// <param name="size">The size.</param>
        /// <returns>the target DSP address.</returns>
        public DspAddress Translate(CpuAddress targetCpuAddress, ulong size)
        {
            if (Contains(targetCpuAddress, size) && IsMapped())
            {
                ulong offset = targetCpuAddress - CpuAddress;

                return DspAddress + offset;
            }

            return 0;
        }

        /// <summary>
        /// Is the <see cref="MemoryPoolState"/> mapped on the DSP?
        /// </summary>
        /// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped on the DSP.</returns>
        public bool IsMapped()
        {
            return DspAddress != 0;
        }
    }
}