using System.Runtime.InteropServices;
using RefreshCS;

namespace Nerfed.Runtime;

public class Shader : Resource
{
    public IntPtr Handle { get; private set; }

    public uint SamplerCount { get; private set; }
    public uint StorageTextureCount { get; private set; }
    public uint StorageBufferCount { get; private set; }
    public uint UniformBufferCount { get; private set; }

    internal Shader() { }

    internal override unsafe void Load(Stream stream)
    {
        using (BinaryReader reader = new BinaryReader(stream))
        {
            Refresh.ShaderCreateInfo createInfo;
            createInfo.Format = (Refresh.ShaderFormat)reader.ReadInt32();
            createInfo.Stage = (Refresh.ShaderStage)reader.ReadInt32();
            createInfo.UniformBufferCount = (uint)reader.ReadInt32();
            createInfo.StorageBufferCount = (uint)reader.ReadInt32();
            createInfo.StorageTextureCount = (uint)reader.ReadInt32();
            createInfo.SamplerCount = (uint)reader.ReadInt32();

            int byteCodeSize = reader.ReadInt32();
            void* byteCodeBuffer = NativeMemory.Alloc((nuint)byteCodeSize);
            Span<byte> byteCodeSpan = new Span<byte>(byteCodeBuffer, byteCodeSize);

            int bytesRead = 0;
            while (bytesRead < byteCodeSize)
            {
                bytesRead += reader.Read(byteCodeSpan.Slice(bytesRead));
            }

            createInfo.CodeSize = (nuint)byteCodeSize;
            createInfo.Code = (byte*)byteCodeBuffer;
            createInfo.EntryPointName = "main";

            Handle = Refresh.Refresh_CreateShader(Engine.GraphicsDevice.Handle, createInfo);
            NativeMemory.Free(byteCodeBuffer);

            SamplerCount = createInfo.SamplerCount;
            StorageTextureCount = createInfo.StorageTextureCount;
            StorageBufferCount = createInfo.StorageBufferCount;
            UniformBufferCount = createInfo.UniformBufferCount;
        }
    }

    internal override void Unload()
    {
        Refresh.Refresh_ReleaseShader(Engine.GraphicsDevice.Handle, Handle);
        Handle = IntPtr.Zero;
    }
}
