using System;
using System.Runtime.CompilerServices;

namespace Quik.OpenGL
{
    public unsafe static partial class GL
    {
        private delegate void EnableVertexAttribArrayProc(int location);
        private delegate void VertexAttribPointerProc(int location, int size, GLEnum type, bool normalized, int stride, IntPtr offset);
        private delegate void VertexAttribIPointerProc(int location, int size, GLEnum type, int stride, IntPtr offset);

        private static GenObjectsProc _genVertexArrays;
        private static GenObjectsProc _deleteVertexArrays;
        private static BindObjectProc _bindVertexArray;
        private static EnableVertexAttribArrayProc _enableVertexAttribArray;
        private static EnableVertexAttribArrayProc _disableVertexAttribArray;
        private static VertexAttribPointerProc _vertexAttribPointer;
        private static VertexAttribIPointerProc _vertexAttribIPointer;

        private static void LoadVertexArrays()
        {
            _genVertexArrays = GetProcAddress<GenObjectsProc>("glGenVertexArrays");
            _deleteVertexArrays = GetProcAddress<GenObjectsProc>("glDeleteVertexArrays");
            _bindVertexArray = GetProcAddress<BindObjectProc>("glBindVertexArray");
            _enableVertexAttribArray = GetProcAddress<EnableVertexAttribArrayProc>("glEnableVertexAttribArray");
            _disableVertexAttribArray = GetProcAddress<EnableVertexAttribArrayProc>("glDisableVertexAttribArray");
            _vertexAttribPointer = GetProcAddress<VertexAttribPointerProc>("glVertexAttribPointer");
            _vertexAttribIPointer = GetProcAddress<VertexAttribIPointerProc>("glVertexAttribIPointer");
        }

        [MethodImpl(AggressiveInlining)]
        public static void GenVertexArrays(int count, out int vertexArrays)
        {
            fixed (int *ptr = &vertexArrays)
                _genVertexArrays(count, ptr);
        }

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

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

        [MethodImpl(AggressiveInlining)]
        public static void DeleteVertexArrays(int count, ref int vertexArrays)
        {
            fixed (int *ptr = &vertexArrays)
                _deleteVertexArrays(count, ptr);
        }

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

        [MethodImpl(AggressiveInlining)]
        public static void DeleteVertexArray(int vertexArray) => DeleteVertexArrays(1, ref vertexArray);

        [MethodImpl(AggressiveInlining)]
        public static void BindVertexArray(int vertexArray) => _bindVertexArray(vertexArray);

        [MethodImpl(AggressiveInlining)]
        public static void EnableVertexAttribArray(int location) => _enableVertexAttribArray(location);

        [MethodImpl(AggressiveInlining)]
        public static void DisableVertexAttribArray(int location) => _disableVertexAttribArray(location);

        [MethodImpl(AggressiveInlining)]
        public static void VertexAttribPointer(int location, int size, GLEnum type, bool normalized, int stride, int offset) =>
            _vertexAttribPointer(location, size, type, normalized, stride, (IntPtr)offset);

        [MethodImpl(AggressiveInlining)]
        public static void VertexAttribIPointer(int location, int size, GLEnum type, int stride, int offset) =>
            _vertexAttribIPointer(location, size, type, stride, (IntPtr)offset);
    }
}