#include "stdafx.h"
#include "DeviceCairoGL.h"
#include "Exception.h"
#include "RenderMgr.h"
#include "2D/Cairo/GLSurface.h"
#include "2D/Cairo/TextMgr.h"

#ifdef GUI_ENABLE_CAIRO_GL

namespace gui {

	CairoGLContext::CairoGLContext(GLDevice *owner, GdkWindow *window, GdkGLContext *context)
		: Context(owner, window, context), device(null) {

		gdk_gl_context_make_current(context);

		// Try to get the current context and create a Cairo device for it.
		GdkDisplay *gdkDisplay = gdk_window_get_display(window);
		if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
			// Try EGL.
			GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY(gdkDisplay);
			struct wl_display *wlDisplay = gdk_wayland_display_get_wl_display(display);
			if (EGLContext eglContext = eglGetCurrentContext()) {
				device = cairo_egl_device_create(eglGetDisplay((NativeDisplayType)wlDisplay), eglContext);
			}
		} else if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
			// Try GLX first, and then EGL. Note: It seems like Gtk+ does not attempt to use EGL on X11 at the moment.
			Display *display = GDK_DISPLAY_XDISPLAY(gdkDisplay);
			if (GLXContext glxContext = glXGetCurrentContext()) {
				device = cairo_glx_device_create(display, glxContext);
			} else if (EGLContext eglContext = eglGetCurrentContext()) {
				device = cairo_egl_device_create(eglGetDisplay(display), eglContext);
			}
		}

		if (!device) {
			StrBuf *msg = new (owner->e) StrBuf();
			*msg << S("Initialization of OpenGL failed: Failed to extract the current GL context.\n");
			*msg << S("Try setting the environment variable ") << S(ENV_RENDER_BACKEND) << S(" to \"gtk\" or \"software\".");
			throw new (owner->e) GuiError(msg->toS());
		}

		gdk_gl_context_clear_current();
	}

	CairoGLContext::~CairoGLContext() {
		gdk_gl_context_clear_current();
		gdk_gl_context_make_current(context);
		cairo_device_destroy(device);
	}

	CairoGLDevice::CairoGLDevice(Engine &e) : GLDevice(e) {}

	CairoGLContext *CairoGLDevice::createContext(GdkWindow *window, GdkGLContext *context) {
		return new CairoGLContext(this, window, context);
	}

	Surface2D *CairoGLDevice::createSurface2D(GtkWidget *widget, Context *context) {
		int scale = gtk_widget_get_scale_factor(widget);
		Size size(gtk_widget_get_allocated_width(widget),
				gtk_widget_get_allocated_height(widget));

		if (context->id == 0) {
			context->id = renderMgr(e)->allocId();
		}

		return new CairoGLSurface(size, Float(scale), static_cast<CairoGLContext *>(context));
	}

	TextMgr *CairoGLDevice::createTextMgr() {
		return new CairoText();
	}

}

#endif
