using System;
using System.Runtime.CompilerServices;

namespace Quik.OpenGL
{
    public unsafe static partial class GL
    {
        private delegate void BufferDataProc(GLEnum target, int size, void* data, GLEnum usageHint);

        private static GenObjectsProc _genBuffers;
        private static GenObjectsProc _deleteBuffers;
        private static BindSlottedProc _bindBuffer;
        private static BufferDataProc _bufferData;

        private static void LoadBuffer()
        {
            _genBuffers    = GetProcAddress<GenObjectsProc>("glGenBuffers");
            _deleteBuffers = GetProcAddress<GenObjectsProc>("glDeleteBuffers");
            _bindBuffer    = GetProcAddress<BindSlottedProc>("glBindBuffer");
            _bufferData    = GetProcAddress<BufferDataProc>("glBufferData");
        }

        [MethodImpl(AggressiveInlining)]
        public static void GenBuffers(int count, out int buffers)
        {
            fixed (int *ptr = &buffers)
                _genBuffers(count, ptr);
        }

        [MethodImpl(AggressiveInlining)]
        public static void GenBuffers(int[] buffers) => GenBuffers(buffers.Length, out buffers[0]);

        [MethodImpl(AggressiveInlining)]
        public static int GenBuffer()
        {
            GenBuffers(1, out int i);
            return i;
        }

        [MethodImpl(AggressiveInlining)]
        public static void DeleteBuffers(int count, ref int buffers)
        {
            fixed (int *ptr = &buffers)
                _deleteBuffers(count, ptr);
        }

        [MethodImpl(AggressiveInlining)]
        public static void DeleteBuffers(int[] buffers) => DeleteBuffers(buffers.Length, ref buffers[0]);

        [MethodImpl(AggressiveInlining)]
        public static void DeleteBuffer(int buffer) => DeleteBuffers(1, ref buffer);

        [MethodImpl(AggressiveInlining)]
        public static void BindBuffer(GLEnum target, int buffer)
        {
            _bindBuffer(target, buffer);
        }

        [MethodImpl(AggressiveInlining)]
        public static void BufferData(GLEnum target, int size, IntPtr data, GLEnum usageHint) =>
            _bufferData(target, size, (void*)data, usageHint);

        [MethodImpl(AggressiveInlining)]
        public static void BufferData<T>(GLEnum target, int size, ref T data, GLEnum usageHint)
            where T : unmanaged
        {
            fixed (T* ptr = &data)
                _bufferData(target, size, ptr, usageHint);
        }

        [MethodImpl(AggressiveInlining)]
        public static void BufferData<T>(GLEnum target, int size, T[] data, GLEnum usageHint)
            where T : unmanaged =>
            BufferData(target, size, ref data[0], usageHint);

    }
}