#include "egl_ctx.h"

#include <stdio.h>
#include <stdlib.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>

#define log_trace printf
#define log_debug printf
#define log_info printf
#define log_warn printf
#define log_error printf
#define log_fatal printf

struct egl_ctx
{
    EGLDisplay egl_display;
    EGLContext egl_context;
    EGLSurface egl_surface;
};

static void egl_ctx_print_config(EGLDisplay egl_display, EGLConfig egl_conf);

int egl_ctx_create(egl_ctx_t **pp_ctx, void *window_ctx)
{
    egl_ctx_t *ctx = (egl_ctx_t *)malloc(sizeof(egl_ctx_t));
    if (ctx == NULL)
    {
        log_error("Call malloc() failed.\n");
        return -1;
    }

    EGLDisplay egl_display = eglGetDisplay(NULL);
    if (egl_display == EGL_NO_DISPLAY)
    {
        log_fatal("Call eglGetDisplay() failed. error: %d\n", eglGetError());
        goto FAILED;
    }

    EGLint egl_version_major, egl_version_minor;
    if (!eglInitialize(egl_display, &egl_version_major, &egl_version_minor))
    {
        log_fatal("Call eglInitialize() failed. error: %d\n", eglGetError());
        goto FAILED;
    }

    log_info("Initialized EGL version %d.%d\n", egl_version_major, egl_version_minor);

    EGLint egl_config_constraints[] = {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 0,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_CONFIG_CAVEAT, EGL_NONE,
        EGL_NONE};

    EGLint num_config;
    EGLConfig egl_conf;
    if (!eglChooseConfig(egl_display, egl_config_constraints, &egl_conf, 1, &num_config))
    {
        log_fatal("Call eglChooseConfig() failed. error: %d\n", eglGetError());
        goto FAILED;
    }

    EGLint ctx_attr[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE};

    EGLContext egl_context = eglCreateContext(egl_display, egl_conf, EGL_NO_CONTEXT, ctx_attr);
    if (egl_context == EGL_NO_CONTEXT)
    {
        log_fatal("Call eglCreateContext() failed. error: %d\n", eglGetError());
        goto FAILED;
    }

    EGLSurface egl_surface = eglCreateWindowSurface(egl_display, egl_conf, (EGLNativeWindowType)window_ctx, NULL);
    if (egl_surface == EGL_NO_SURFACE)
    {
        log_fatal("Call eglCreateWindowSurface() failed. error: %d)", eglGetError());
        goto FAILED;
    }

    eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);

    egl_ctx_print_config(egl_display, egl_conf);

    ctx->egl_display = egl_display;
    ctx->egl_context = egl_context;
    ctx->egl_surface = egl_surface;
    *pp_ctx = ctx;

    return 0;

FAILED:
    if (egl_context != EGL_NO_CONTEXT)
    {
        eglDestroyContext(egl_display, egl_context);
    }
    if (egl_surface != EGL_NO_SURFACE)
    {
        eglDestroySurface(egl_display, egl_surface);
    }
    if (ctx != NULL)
    {
        free(ctx);
    }
    return -1;
}

void egl_ctx_release(egl_ctx_t **pp_ctx)
{
    if (pp_ctx && *pp_ctx)
    {
        egl_ctx_t *ctx = *pp_ctx;
        *pp_ctx = NULL;
        eglDestroyContext(ctx->egl_display, ctx->egl_context);
        eglDestroySurface(ctx->egl_display, ctx->egl_surface);
        free(ctx);
    }
}

void egl_ctx_make_current(egl_ctx_t *ctx)
{
    if (ctx)
    {
        eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context);
    }
}

void egl_ctx_swap_buffers(egl_ctx_t *ctx)
{
    if (ctx)
    {
        eglSwapBuffers(ctx->egl_display, ctx->egl_surface);
    }
}

static void egl_ctx_print_config(EGLDisplay egl_display, EGLConfig egl_conf)
{
    log_info("EGL Client APIs: %s\n", eglQueryString(egl_display, EGL_CLIENT_APIS));
    log_info("EGL Vendor: %s\n", eglQueryString(egl_display, EGL_VENDOR));
    log_info("EGL Version: %s\n", eglQueryString(egl_display, EGL_VERSION));
    // log_info("EGL Extensions: %s\n", eglQueryString(egl_display, EGL_EXTENSIONS));

    int i = -1;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_CONFIG_ID, &i);
    log_debug("EGL_CONFIG_ID = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_RED_SIZE, &i);
    log_debug("EGL_RED_SIZE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_GREEN_SIZE, &i);
    log_debug("EGL_GREEN_SIZE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_BLUE_SIZE, &i);
    log_debug("EGL_BLUE_SIZE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_ALPHA_SIZE, &i);
    log_debug("EGL_ALPHA_SIZE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_DEPTH_SIZE, &i);
    log_debug("EGL_DEPTH_SIZE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_LEVEL, &i);
    log_debug("EGL_LEVEL = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_NATIVE_RENDERABLE, &i);
    log_debug("EGL_NATIVE_RENDERABLE = %s\n", i ? "EGL_TRUE" : "EGL_FALSE");

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_NATIVE_VISUAL_TYPE, &i);
    log_debug("EGL_NATIVE_VISUAL_TYPE = %d\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_RENDERABLE_TYPE, &i);
    log_debug("EGL_RENDERABLE_TYPE = 0x%04x\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_SURFACE_TYPE, &i);
    log_debug("EGL_SURFACE_TYPE = 0x%04x\n", i);

    i = 0;
    eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_TYPE, &i);
    if (i == EGL_TRANSPARENT_RGB)
    {
        log_debug("EGL_TRANSPARENT_TYPE = EGL_TRANSPARENT_RGB\n");

        i = 0;
        eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_RED_VALUE, &i);
        log_debug("EGL_TRANSPARENT_RED = 0x%02x\n", i);

        i = 0;
        eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_GREEN_VALUE, &i);
        log_debug("EGL_TRANSPARENT_GREEN = 0x%02x\n", i);

        i = 0;
        eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_BLUE_VALUE, &i);
        log_debug("EGL_TRANSPARENT_BLUE = 0x%02x\n", i);
    }
    else
    {
        log_debug("EGL_TRANSPARENT_TYPE = EGL_NONE\n");
    }
}