{% import "utils.j2" as u with context -%}
{{ preamble }}

#ifndef GLOAM_{{ guard }}
#define GLOAM_{{ guard }}
{# ---- System-header conflict guards ---------------------------------------
   These prevent including both the platform/system GL or Vulkan headers and
   this gloam header, which would cause duplicate declarations.  Using
   reserved identifiers (double-underscore prefixed) requires suppressing the
   clang warning for that specific pragma block. #}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] %}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif
{%- for api in fs.apis %}
{%- if api in ["gl", "glcore"] %}
{{ u.header_error("__gl_h_",     "OpenGL (gl.h)",     api) }}
{{ u.header_error("__glext_h_",  "OpenGL (glext.h)",  api) }}
{%- elif api == "gles1" %}
{{ u.header_error("__gl_h_",       "OpenGL ES 1 (gl.h)",   api) }}
{{ u.header_error("__gles1_gl_h_", "OpenGL ES 1",          api) }}
{%- elif api == "gles2" %}
{{ u.header_error("__gl2_h_",       "OpenGL ES 2 (gl2.h)", api) }}
{{ u.header_error("__gles2_gl2_h_", "OpenGL ES 2",         api) }}
{{ u.header_error("__gl3_h_",       "OpenGL ES 3 (gl3.h)", api) }}
{{ u.header_error("__gles2_gl3_h_", "OpenGL ES 3",         api) }}
{%- endif %}
{%- endfor %}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
{%- elif fs.spec_name == "vk" %}
{{ u.header_error("VULKAN_H_",      "Vulkan (vulkan.h)",      "vulkan") }}
{{ u.header_error("VULKAN_CORE_H_", "Vulkan (vulkan_core.h)", "vulkan") }}
{%- elif fs.spec_name == "egl" %}
{{ u.header_error("__egl_h_", "EGL (egl.h)", "egl") }}
{%- elif fs.spec_name == "glx" %}
{{ u.header_error("GLX_H", "GLX (glx.h)", "glx") }}
{%- elif fs.spec_name == "wgl" %}
{{ u.header_error("__wgl_h_", "WGL (wgl.h)", "wgl") }}
{%- endif %}
{% if fs.spec_name == "glx" %}
#ifdef __linux

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <gloam/gl.h>
{% elif fs.spec_name == "wgl" %}
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#include <gloam/gl.h>
/* These are defined in wingdi.h and we're redefining them. */
#undef wglUseFontBitmaps
#undef wglUseFontOutlines
{% endif %}
#include <stddef.h>
#include <stdint.h>

{% for hdr in fs.required_headers -%}
#include "{{ hdr }}"
{% endfor -%}

{# Platform / WSI system headers: include-category types whose protection
   guards were inferred at generation time from which extensions require them.
   An empty protect list means the include is unconditional. #}
{%- for type in fs.types -%}
{% if type.category == "include" and type.raw_c -%}
{{ u.protect_begin(type.protect) -}}
{{ type.raw_c }}
{{ u.protect_end(type.protect) -}}
{% endif -%}
{%- endfor %}
#ifdef __cplusplus
extern "C" {
#endif

{% if fs.is_gl_family -%}
/* Calling convention */
#ifndef APIENTRY
#  if defined(_WIN32)
#    define APIENTRY __stdcall
#  else
#    define APIENTRY
#  endif
#endif
#ifndef APIENTRYP
#  define APIENTRYP APIENTRY *
#endif
#ifndef GLAPI
#  define GLAPI extern
#endif

/* Calling convention for GL callback function pointers (e.g. debug callbacks,
   blob cache functions).  Piggybacks on APIENTRY if already defined so that
   code which defines APIENTRY before including this header gets consistent
   behaviour.  Guard against redefinition so multiple gloam headers included
   in the same translation unit don't conflict. */
#ifndef GLOAM_API_PTR
#  ifdef APIENTRY
#    define GLOAM_API_PTR APIENTRY
#  elif defined(_WIN32)
#    define GLOAM_API_PTR __stdcall
#  else
#    define GLOAM_API_PTR
#  endif
#endif
#ifndef GLAPIENTRY
#  define GLAPIENTRY GLOAM_API_PTR
#endif
{%- endif %}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] %}

/* Forward declarations for OpenCL interop types. */
struct _cl_context;
struct _cl_event;

{% endif %}
{%- if fs.features | length > 0 -%}
/* ---- Version feature guards ----------------------------------------------
   These mirror the upstream vulkan_core.h / gl.h definitions so that code
   guarded by e.g. #ifdef GL_VERSION_3_3 compiles correctly against this
   header. */
{% for feat in fs.features -%}
#define {{ feat.full_name }} 1
{% endfor -%}
{% endif -%}
{%- if not fs.is_vulkan and fs.extensions | length > 0 %}
/* ---- Extension compile-time guards ---------------------------------------
   These mirror the definitions in standard glext.h/gl2ext.h/eglext.h so
   that code guarded by e.g. #ifdef GL_ARB_draw_indirect compiles correctly
   against this header. */
{% for ext in fs.extensions %}
{{- u.protect_begin(ext.protect) -}}
#define {{ ext.name }} 1
{{ u.protect_end(ext.protect) -}}
{%- endfor %}
{% endif -%}
{%- if fs.flat_enums | length > 0 -%}
/* ---- Constants ----------------------------------------------------------- */
{% for enum in fs.flat_enums -%}
#define {{ enum.name }} {{ enum.value }}{% if enum.comment %} /* {{ enum.comment }} */{% endif %}
{% endfor -%}
{% endif -%}
{%- if fs.enum_groups | length > 0 %}
/* ---- Vulkan enum groups -------------------------------------------------- */
{% for group in fs.enum_groups %}
{%- if group.bitwidth == 64 %}
/* {{ group.name }} — 64-bit bitmask.
   C++/C23: emitted as an enum with explicit underlying type so all values
   are strongly typed and the tag name is valid for typedef aliases.
   Pre-C23 C: emitted as typedef uint64_t + static const, since C99/C11 do
   not support fixed underlying types for enums. */
#if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
typedef enum {{ group.name }} : uint64_t {
{%- for val in group.values %}
    {{ val.name }} = {{ val.value }}{% if not loop.last %},{% endif %}{% if val.comment %} /* {{ val.comment }} */{% endif %}
{%- endfor %}
} {{ group.name }};
#else
typedef uint64_t {{ group.name }};
{%- for val in group.values %}
static const {{ group.name }} {{ val.name | rjust(48) }} = {{ val.value }};{% if val.comment %} /* {{ val.comment }} */{% endif %}
{%- endfor %}
#endif

{%- else %}
typedef enum {{ group.name }} {
{%- for val in group.values %}
    {{ val.name }} = {{ val.value }}{% if val.comment %} /* {{ val.comment }} */{% endif %},
{%- endfor %}
    {{ group.name | vk_max_enum_name }} = 0x7FFFFFFF
} {{ group.name }};
{% endif -%}
{% endfor -%}
{% endif %}
/* ---- Types ----------------------------------------------------------------
   Emitted in topological dependency order. */
{% for type in fs.types -%}
{%- if type.category != "include" and type.raw_c %}
{{ u.protect_begin(type.protect if type.protect else []) -}}
{{ type.raw_c }}
{{ u.protect_end(type.protect if type.protect else []) -}}
{% endif -%}
{% endfor %}

/* ---- PFN typedefs -------------------------------------------------------- */
{% if fs.is_gl_family -%}
{% for cmd in fs.commands -%}
{{ u.cmd_protect_begin(cmd) -}}
typedef {{ cmd.return_type }} (APIENTRYP {{ cmd.pfn_type }})({{ cmd.params_str }});
{{ u.cmd_protect_end(cmd) }}
{%- endfor %}
{% else %}
{% for cmd in fs.commands %}
{{ u.cmd_protect_begin(cmd) -}}
typedef {{ cmd.return_type }} (VKAPI_PTR *{{ cmd.pfn_type }})({{ cmd.params_str }});
{{- u.cmd_protect_end(cmd) }}
{% endfor %}
{% endif %}

/* ---- Context struct ------------------------------------------------------
   Three anonymous unions give both indexed (array) and named (struct member)
   access to the same memory, at zero runtime cost.

   featArray / extArray: unsigned char flags, one per feature / extension.
   pfnArray: function pointer slots, one per command.

   Anonymous structs inside the unions are a C11 / GCC extension; they are
   universally supported on our target compilers. */
typedef struct {{ fs.context_name }} {
    union {
        unsigned char featArray[{{ fs.features | length }}];
        struct {
{%- for feat in fs.features %}
        /* {{ feat.index | rjust(4) }} */ unsigned char {{ feat.short_name }};
{%- endfor %}
        };
    };
{% if fs.extensions | length > 0 %}
    union {
        unsigned char extArray[{{ fs.extensions | length }}];
        struct {
{%- for ext in fs.extensions %}
        /* {{ ext.index | rjust(4) }} */ unsigned char {{ ext.short_name | c_ident }};
{%- endfor %}
        };
    };
{% endif %}
    union {
        void *pfnArray[{{ fs.commands | length }}];
        struct {
{%- for cmd in fs.commands %}
{%- if cmd.protect %}
#ifdef {{ cmd.protect }}
        /* {{ cmd.index | rjust(4) }} */ {{ cmd.pfn_type }} {{ cmd.short_name }};
#else
        /* {{ cmd.index | rjust(4) }} */ void *_pad{{ cmd.index }};
#endif /* {{ cmd.protect }} */
{% else %}
        /* {{ cmd.index | rjust(4) }} */ {{ cmd.pfn_type }} {{ cmd.short_name }};
{%- endif %}
{%- endfor %}
        };
    };
    /* Built-in loader library handle.  Set by gloamLoaderLoad*Context when it
       opens the platform library.  Pre-populate before calling that function
       to supply your own handle (dlopen is then skipped); zero it before
       calling gloamLoaderUnload*Context to prevent it closing a handle you
       still own.  Present on all context types so user code can use a single
       field name regardless of API. */
    void *gloam_loader_handle;

{% if fs.is_vulkan %}
    /* Vulkan-specific metadata — not part of any union.
       These fields make repeated gloamLoadVulkanContext calls additive:
       each call only loads what the current set of live handles can provide,
       and subsequent calls fill in the rest without wiping previous work. */
    uint32_t vk_instance_version;    /* cached EnumerateInstanceVersion result        */
    uint32_t vk_device_version;      /* cached GetPhysicalDeviceProperties.apiVersion */
    uint8_t  vk_found_instance_exts; /* set once instance extensions enumerated       */
    uint8_t  vk_found_device_exts;   /* set once device extensions enumerated         */
{% endif %}
} {{ fs.context_name }};

/* Global context instance — a value, not a pointer, so the compiler knows
   its address is fixed and does not re-load it on every access. */
extern {{ fs.context_name }} gloam_{{ fs.spec_name }}_context;

/* ---- Feature presence macros --------------------------------------------
   Test whether a versioned feature was detected at load time. */
{%- for feat in fs.features %}
#define GLOAM_{{ feat.full_name }} (gloam_{{ fs.spec_name }}_context.{{ feat.short_name }})
{%- endfor %}

/* ---- Extension presence macros ------------------------------------------ */
{% for ext in fs.extensions %}
{{- u.protect_begin(ext.protect) -}}
#define GLOAM_{{ ext.name }} (gloam_{{ fs.spec_name }}_context.{{ ext.short_name | c_ident }})
{{ u.protect_end(ext.protect) -}}
{%- endfor %}

/* ---- Dispatch macros and IntelliSense prototypes -------------------------
   Under __INTELLISENSE__ we expose real function declarations so IDEs can
   provide parameter info and type checking.  In normal compilation the macros
   dispatch directly through the global context struct. */
#ifdef __INTELLISENSE__
{%- for cmd in fs.commands %}
{{ u.cmd_protect_begin(cmd) -}}
{{ cmd.return_type }} {{ cmd.name }}({{ cmd.params_str }});
{{- u.cmd_protect_end(cmd) }}
{%- endfor %}
#else
{%- for cmd in fs.commands %}
{{ u.cmd_protect_begin(cmd) -}}
#define {{ cmd.name }} (gloam_{{ fs.spec_name }}_context.{{ cmd.short_name }})
{{- u.cmd_protect_end(cmd) }}
{%- endfor %}
#endif /* __INTELLISENSE__ */

/* ---- API declarations ---------------------------------------------------- */

/* Opaque function pointer type — the common return type for all load
   callbacks.  Callers cast to the specific PFN type they need. */
typedef void (*GloamAPIProc)(void);

/* Load function pointer type (GL / EGL / GLX / WGL). */
typedef GloamAPIProc (*GloamLoadFunc)(const char *name);
{%- if fs.is_vulkan %}
/* Vulkan command scope — controls which proc-addr entry point the load
   callback should use.  Must match the definition in impl_util.j2. */
typedef enum {
    GloamCommandScopeUnknown  = 0,
    GloamCommandScopeGlobal   = 1,
    GloamCommandScopeInstance = 2,
    GloamCommandScopeDevice   = 3
} GloamCommandScope;

typedef GloamAPIProc (*GloamVkLoadFunc)(const char *name, GloamCommandScope scope);
typedef GloamAPIProc (*GloamVkUserptrLoadFunc)(void *userptr, const char *name, GloamCommandScope scope);
{% endif -%}
{% for api in fs.apis -%}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}GloamLoadFunc getProcAddr);
int gloamLoad{{ api | api_display }}(GloamLoadFunc getProcAddr);
{%- elif fs.spec_name == "egl" %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}EGLDisplay display, GloamLoadFunc getProcAddr);
int gloamLoad{{ api | api_display }}(EGLDisplay display, GloamLoadFunc getProcAddr);
{%- elif fs.spec_name == "glx" %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}Display *display, int screen, GloamLoadFunc getProcAddr);
int gloamLoad{{ api | api_display }}(Display *display, int screen, GloamLoadFunc getProcAddr);
{%- elif fs.spec_name == "wgl" %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}HDC hdc, GloamLoadFunc getProcAddr);
int gloamLoad{{ api | api_display }}(HDC hdc, GloamLoadFunc getProcAddr);
{%- elif fs.spec_name == "vk" %}
/* UserPtr variant: canonical form; the load callback receives an opaque
   userptr so C callers can thread context (instance, device) through it. */
int gloamLoadVulkanContextUserPtr({{ u.ctx_arg(', ') }}VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, GloamVkUserptrLoadFunc load, void *userptr);
int gloamLoadVulkanUserPtr(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, GloamVkUserptrLoadFunc load, void *userptr);
/* Plain variant: for callers that can capture state in the callback itself. */
int gloamLoadVulkanContext({{ u.ctx_arg(', ') }}VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, GloamVkLoadFunc load);
int gloamLoadVulkan(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, GloamVkLoadFunc load);
{% endif %}
{% endfor -%}

{%- if loader -%}
/* Built-in loader: opens the platform library if needed and calls Load for you.
   Each Load function may be called multiple times (additive, same contract as
   the underlying gloamLoad* functions).  Pre-populate context->gloam_loader_handle
   to supply your own library handle; zero it before Unload to retain ownership. */
{%- for api in fs.apis %}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] %}
int  gloamLoaderLoad{{ api | api_display }}Context({{ u.ctx_arg() }});
int  gloamLoaderLoad{{ api | api_display }}(void);
void gloamLoaderUnload{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderUnload{{ api | api_display }}(void);
void gloamLoaderReset{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderReset{{ api | api_display }}(void);
{%- elif fs.spec_name == "egl" %}
int  gloamLoaderLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}EGLDisplay display);
int  gloamLoaderLoad{{ api | api_display }}(EGLDisplay display);
void gloamLoaderUnload{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderUnload{{ api | api_display }}(void);
void gloamLoaderReset{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderReset{{ api | api_display }}(void);
{%- elif fs.spec_name == "glx" %}
int  gloamLoaderLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}Display *display, int screen);
int  gloamLoaderLoad{{ api | api_display }}(Display *display, int screen);
void gloamLoaderUnload{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderUnload{{ api | api_display }}(void);
void gloamLoaderReset{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderReset{{ api | api_display }}(void);
{%- elif fs.spec_name == "wgl" %}
int  gloamLoaderLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}HDC hdc);
int  gloamLoaderLoad{{ api | api_display }}(HDC hdc);
void gloamLoaderUnload{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderUnload{{ api | api_display }}(void);
void gloamLoaderReset{{ api | api_display }}Context({{ u.ctx_arg() }});
void gloamLoaderReset{{ api | api_display }}(void);
{%- elif fs.spec_name == "vk" %}
int  gloamLoaderLoadVulkanContext({{ u.ctx_arg(', ') }}VkInstance instance, VkPhysicalDevice physical_device, VkDevice device);
int  gloamLoaderLoadVulkan(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device);
void gloamLoaderUnloadVulkanContext({{ u.ctx_arg() }});
void gloamLoaderUnloadVulkan(void);
void gloamLoaderResetVulkanContext({{ u.ctx_arg() }});
void gloamLoaderResetVulkan(void);
{%- endif %}
{% endfor -%}
{% endif %}
#ifdef __cplusplus
}
#endif
{% if fs.spec_name == "glx" %}
#endif /* __linux */
{% elif fs.spec_name == "wgl" %}
#endif /* _WIN32 */
{% endif %}
#endif /* GLOAM_{{ guard }} */
