#include "mr4th_osw.ogl.egl.h"

////////////////////////////////
// EGL Implementation: OSW OGL Functions

static OSW_Backend osw_ogl_egl_backend = {0};
MR4TH_SYMBOL OSW_Backend*
osw_ogl_backend_open(void){
  OSW_Backend *backend = 0;
  if (!osw_ogl_egl_backend.initialized){
    if (osw__egl_open()){
      backend = &osw_ogl_egl_backend;
      backend->initialized = 1;
      backend->close = osw__egl_close;
      backend->begin = osw__egl_begin;
      backend->end   = osw__egl_end;
    }
  }
  return(backend);
}

////////////////////////////////
// EGL Functions

static OSW_EGL_Ctx    osw__egl_ctx = {0};
static OSW_EGL_Window osw__egl_windows[MR4TH_OSW_MAX_WINDOW_COUNT] = {0};

static B32
osw__egl_open(void){
  // setup egl display
  EGLDisplay egl_display = 0;
  {
#if OSW_IMPL_X11
    egl_display =
      eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, osw__x11_ctx.display, 0);
#elif OSW_IMPL_WAYLAND
    egl_display =
      eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, osw__wl_ctx.wl_display, 0);
#else
# error osw implementation does not support EGL backend
#endif
  }
  
  // setup egl context
  EGLContext egl_context = 0;
  if (egl_display != 0){
    EGLint major = 0;
    EGLint minor = 0;
    eglInitialize(egl_display, &major, &minor);
    eglBindAPI(EGL_OPENGL_API);
    
    // setup egl context
    EGLint attributes[] = {
      EGL_CONTEXT_MAJOR_VERSION, 3,
      EGL_CONTEXT_MINOR_VERSION, 3,
      EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
      EGL_NONE,
    };
    egl_context =
      eglCreateContext(egl_display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT,
                       attributes);
  }
  
  // load opengl functions
  B32 load_opengl_success = 0;
  if (egl_context != 0){
    eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context);
    load_opengl_success = 1;
#define X(N,R,P) \
N = (R(*)P)(eglGetProcAddress(#N)); \
if (N == 0) load_opengl_success = 0;
#include "mr4th_ogl.funcs.h"
#undef X
  }
  
  // on success save the new egl context
  if (load_opengl_success){
    osw__egl_ctx.egl_display = egl_display;
    osw__egl_ctx.egl_context = egl_context;
  }
  
  // on failure emit an error and cleanup
  if (!load_opengl_success){
    er_emit_lit("osw: failed to open egl backend");
    if (egl_context != 0){
      eglDestroyContext(egl_display, egl_context);
      egl_context = 0;
    }
    if (egl_display != 0){
      eglTerminate(egl_display);
      egl_display = 0;
    }
  }
  
  return(load_opengl_success);
}

static void
osw__egl_close(void){
  // TODO(allen): clear window states
  if (osw__egl_ctx.egl_context != 0){
    eglDestroyContext(osw__egl_ctx.egl_display, osw__egl_ctx.egl_context);
    osw__egl_ctx.egl_context = 0;
  }
  if (osw__egl_ctx.egl_display != 0){
    eglTerminate(osw__egl_ctx.egl_display);
    osw__egl_ctx.egl_display = 0;
  }
}

static void
osw__egl_begin(OSW_Window *window){
  if (window == 0){
    eglMakeCurrent(osw__egl_ctx.egl_display,
                   EGL_NO_SURFACE, EGL_NO_SURFACE,
                   osw__egl_ctx.egl_context);
  }
  else{
    U32 idx = osw_window__idx_from_ptr(window);
    OSW_EGL_Window *egl_window = &osw__egl_windows[idx];
    B32 did_make_current = 0;
    
    // initialize if null
    if (!egl_window->activated){
      // get configs
      EGLConfig configs[64];
      EGLint config_cap = sizeof(configs)/sizeof(*configs);
      EGLint config_count = 0;
      {
        EGLint attributes[] = {
          EGL_SURFACE_TYPE,      EGL_WINDOW_BIT,
          EGL_CONFORMANT,        EGL_OPENGL_BIT,
          EGL_RENDERABLE_TYPE,   EGL_OPENGL_BIT,
          EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
          EGL_RED_SIZE,      8,
          EGL_GREEN_SIZE,    8,
          EGL_BLUE_SIZE,     8,
          EGL_DEPTH_SIZE,   24,
          EGL_STENCIL_SIZE,  8,
          EGL_NONE,
        };
        eglChooseConfig(osw__egl_ctx.egl_display, attributes,
                        configs, config_cap, &config_count);
      }
      
#if OSW_IMPL_X11
      // TODO(allen): 
#elif OSW_IMPL_WAYLAND
      struct wl_egl_window  *main_wl_egl_window =
        wl_egl_window_create(window->main_wl_surface,
                             window->dim.x, window->dim.y);
      
      EGLSurface main_egl_surface = 0;
      if (main_wl_egl_window != 0){
        EGLAttrib attributes[] = {
          EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_SRGB,
          EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
          EGL_NONE,
        };
        for (EGLint i = 0; i < config_count; i += 1){
          main_egl_surface =
            eglCreatePlatformWindowSurface(osw__egl_ctx.egl_display, configs[i],
                                           main_wl_egl_window, attributes);
          if (main_egl_surface != EGL_NO_SURFACE){
            break;
          }
        }
      }
      
      if (main_egl_surface != EGL_NO_SURFACE){
        egl_window->activated = 1;
        egl_window->main_egl_surface = main_egl_surface;
        egl_window->main_wl_egl_window = main_wl_egl_window;
      }
#endif
      
    }
    
    if (egl_window->activated){
      // make current
      eglMakeCurrent(osw__egl_ctx.egl_display,
                     egl_window->main_egl_surface, 
                     egl_window->main_egl_surface,
                     osw__egl_ctx.egl_context);
      eglSwapInterval(osw__egl_ctx.egl_display, 1);
      
      // window size
#if OSW_IMPL_WAYLAND
      wl_egl_window_resize(egl_window->main_wl_egl_window,
                           window->dim.x, window->dim.y, 0, 0);
#endif
    }
  }
}

static void
osw__egl_end(OSW_Window *window){
  if (window != 0){
    U32 idx = osw_window__idx_from_ptr(window);
    OSW_EGL_Window *egl_window = &osw__egl_windows[idx];
    if (egl_window->main_egl_surface != 0){
      eglSwapBuffers(osw__egl_ctx.egl_display, egl_window->main_egl_surface);
    }
    
#if OSW_IMPL_X11
    // TODO(allen): 
#elif OSW_IMPL_WAYLAND
    wl_surface_commit(window->main_wl_surface);
#endif
  }
}
