/*
 * Copyright 2025 Rémi Bernon for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef __WINE_OPENGL_DRIVER_H
#define __WINE_OPENGL_DRIVER_H

#include <stdarg.h>
#include <stddef.h>

#include <windef.h>
#include <winbase.h>
#include <wingdi.h>

#include "wine/wgl.h"
#include "wine/debug.h"

struct wgl_pixel_format
{
    PIXELFORMATDESCRIPTOR pfd;
    int swap_method;
    int transparent;
    int pixel_type;
    int draw_to_pbuffer;
    int max_pbuffer_pixels;
    int max_pbuffer_width;
    int max_pbuffer_height;
    int transparent_red_value;
    int transparent_red_value_valid;
    int transparent_green_value;
    int transparent_green_value_valid;
    int transparent_blue_value;
    int transparent_blue_value_valid;
    int transparent_alpha_value;
    int transparent_alpha_value_valid;
    int transparent_index_value;
    int transparent_index_value_valid;
    int sample_buffers;
    int samples;
    int bind_to_texture_rgb;
    int bind_to_texture_rgba;
    int bind_to_texture_rectangle_rgb;
    int bind_to_texture_rectangle_rgba;
    int framebuffer_srgb_capable;
    int float_components;
};

enum opengl_extension
{
#define USE_GL_EXT(x) x,
    ALL_GL_EXTS
    ALL_WGL_EXTS
#undef USE_GL_EXT
    GL_EXTENSION_COUNT,
};

struct opengl_client_context
{
    struct HGLRC__              obj;            /* client object header */
    UINT64                      unix_handle;
    UINT64                      unix_funcs;
    DWORD                       current_tid;                            /* thread that the context is current in */
    GLenum                      last_error;
    int                         major_version;
    int                         minor_version;
    BOOLEAN                     extensions[GL_EXTENSION_COUNT];         /* exposed client extensions */
    UINT32                      extension_count;                        /* size of supported extensions */
    UINT16                      extension_array[GL_EXTENSION_COUNT];    /* array of supported extensions */
};

static inline struct opengl_client_context *opengl_client_context_from_client( HGLRC client_context )
{
    return CONTAINING_RECORD( client_context, struct opengl_client_context, obj );
}

struct opengl_client_pbuffer
{
    struct HPBUFFERARB__        obj;            /* client object header */
    UINT64                      unix_handle;
    UINT64                      unix_funcs;
};

static inline struct opengl_client_pbuffer *opengl_client_pbuffer_from_client( HPBUFFERARB client_pbuffer )
{
    return CONTAINING_RECORD( client_pbuffer, struct opengl_client_pbuffer, obj );
}

/* client-side opengl sync */
struct __GLsync
{
    UINT64                      unix_handle;
};

#ifdef WINE_UNIX_LIB

#include "wine/gdi_driver.h"

/* Wine internal opengl driver version, needs to be bumped upon opengl_funcs changes. */
#define WINE_OPENGL_DRIVER_VERSION 37

struct opengl_drawable;

struct opengl_context
{
    HGLRC                       client_context;     /* client side context pointer */
    void                       *driver_private;     /* driver context / private data */
    void                       *internal_context;   /* driver context for win32u internal use */
    int                         format;             /* pixel format of the context */
    struct opengl_drawable     *draw;               /* currently bound draw surface */
    struct opengl_drawable     *read;               /* currently bound read surface */
};

static inline struct opengl_context *opengl_context_from_handle( HGLRC client_context )
{
    struct opengl_client_context *client = opengl_client_context_from_client( client_context );
    return client_context ? (struct opengl_context *)(UINT_PTR)client->unix_handle : NULL;
}

/* interface between opengl32 and win32u */
struct opengl_funcs
{
#define USE_GL_FUNC(x) PFN_##x p_##x;
    ALL_WGL_FUNCS
    ALL_WGL_EXT_FUNCS
    ALL_EGL_FUNCS
    ALL_EGL_EXT_FUNCS
    ALL_GL_FUNCS
    ALL_GL_EXT_FUNCS
#undef USE_GL_FUNC
    void (*p_init_extensions)( BOOLEAN extensions[GL_EXTENSION_COUNT] );
    void (*p_get_pixel_formats)( struct wgl_pixel_format *formats, UINT max_formats, UINT *num_formats, UINT *num_onscreen_formats );
    BOOL (*p_query_renderer)( UINT attribute, void *value );
    BOOL (*p_context_flush)( struct opengl_context *context, void (*flush)(void), UINT flags );
    BOOL (*p_context_create)( struct opengl_context *context, HDC hdc, struct opengl_context *share, const int *attribs );
    BOOL (*p_context_destroy)( struct opengl_context *context );
    BOOL (*p_context_reset)( struct opengl_context *context, struct opengl_context *share, const int *attribs );
    BOOL (*p_pbuffer_create)( HDC hdc, int format, int width, int height, const int *attribs, HPBUFFERARB client_pbuffer );
    void *egl_handle;
};

struct egl_platform
{
    struct list          entry;
    EGLenum              type;
    EGLNativeDisplayType native_display;
    BOOL                 force_pbuffer_formats;

    /* filled by win32u after init_egl_platform */
    unsigned int         index;
    EGLDeviceEXT         device;
    EGLDisplay           display;
    UINT                 config_count;
    EGLConfig           *configs;
    BOOL                 has_EGL_EXT_present_opaque;
    BOOL                 has_EGL_EXT_pixel_format_float;

    /* WGL_WINE_query_renderer info */
    UINT                 device_id;
    UINT                 vendor_id;
    BOOL                 accelerated;
    UINT                 version[3];
    UINT                 core_version;
    UINT                 compat_version;
    UINT                 video_memory;
    const char          *device_name;
    const char          *vendor_name;
    GUID                 device_uuid;
    GUID                 driver_uuid;
};

struct opengl_drawable_funcs
{
    void (*destroy)( struct opengl_drawable *iface );
    /* flush and update the drawable front buffer, called from render thread */
    void (*flush)( struct opengl_drawable *iface, UINT flags );
    /* swap and present the drawable buffers, called from render thread */
    BOOL (*swap)( struct opengl_drawable *iface );
    /* drawable is being unset or made current, called from render thread */
    void (*set_context)( struct opengl_drawable *iface, void *private );
};

/* flags for opengl_drawable flush */
#define GL_FLUSH_FINISHED      0x01
#define GL_FLUSH_INTERVAL      0x02
#define GL_FLUSH_UPDATED       0x04
#define GL_FLUSH_PRESENT       0x08
#define GL_FLUSH_FORCE_SWAP    0x10

/* a driver opengl drawable, either a client surface of a pbuffer */
struct opengl_drawable
{
    const struct opengl_drawable_funcs *funcs;
    LONG                                ref;            /* reference count */
    struct list                         entry;          /* entry in cleanup lists */
    struct client_surface              *client;         /* underlying client surface */
    HDC                                 owner_hdc;      /* HDC owning the drawable, if any (for pbuffer / D3D swapchains) */
    int                                 format;         /* pixel format of the drawable */
    int                                 interval;       /* last set surface swap interval */
    BOOL                                doublebuffer;   /* pixel format is double buffered */
    BOOL                                stereo;         /* pixel format is stereo buffered */
    EGLSurface                          surface;        /* surface for EGL based drivers */
    GLuint                              read_fbo;       /* default read FBO name when emulating framebuffer */
    GLuint                              draw_fbo;       /* default draw FBO name when emulating framebuffer */
    GLenum                              buffer_map[GL_AUX3 - GL_FRONT_LEFT + 1]; /* buffer constants mapping */
};

static inline const char *debugstr_opengl_drawable( struct opengl_drawable *drawable )
{
    if (!drawable) return "(null)";
    return wine_dbg_sprintf( "%s/%p (format %u)", debugstr_client_surface( drawable->client ), drawable, drawable->format );
}

static inline void opengl_drawable_map_buffer( struct opengl_drawable *drawable, GLenum buffer, GLenum set )
{
    drawable->buffer_map[buffer - GL_FRONT_LEFT] = set;
}

W32KAPI void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *funcs, int format, struct client_surface *client );
W32KAPI void opengl_drawable_add_ref( struct opengl_drawable *drawable );
W32KAPI void opengl_drawable_release( struct opengl_drawable *drawable );

W32KAPI void set_window_opengl_drawable( HWND hwnd, struct opengl_drawable *drawable, BOOL current );

/* interface between win32u and the user drivers */
struct opengl_driver_funcs
{
    void (*p_init_egl_platform)(struct egl_platform*);
    void *(*p_get_proc_address)(const char *);
    UINT (*p_init_pixel_formats)(UINT*);
    BOOL (*p_describe_pixel_format)(int,struct wgl_pixel_format*);
    void (*p_init_extensions)( struct opengl_funcs *funcs, BOOLEAN extensions[GL_EXTENSION_COUNT] );
    BOOL (*p_surface_create)( HWND hwnd, int format, struct opengl_drawable **drawable );
    BOOL (*p_context_create)( int format, void *share, const int *attribs, void **context );
    BOOL (*p_context_destroy)(void*);
    BOOL (*p_make_current)( struct opengl_drawable *draw, struct opengl_drawable *read, void *private );
    BOOL (*p_pbuffer_create)( HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target,
                              GLint max_level, GLsizei *width, GLsizei *height, struct opengl_drawable **drawable );
    BOOL (*p_pbuffer_updated)( HDC hdc, struct opengl_drawable *drawable, GLenum cube_face, GLint mipmap_level );
    UINT (*p_pbuffer_bind)( HDC hdc, struct opengl_drawable *drawable, GLenum buffer );
};

#endif /* WINE_UNIX_LIB */

#endif /* __WINE_OPENGL_DRIVER_H */
