// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#include <fennec/langproc/filesystem/file.h>
#include <fennec/lang/startup.h>
#include <fennec/platform/interface/display.h>
#include <fennec/platform/interface/platform.h>
#include <fennec/platform/opengl/egl/context.h>

#include <GL/gl.h>

namespace fennec
{

static gfxcontext* _create_egl_context(display* display) {
	eglcontext* ctx = new eglcontext(display);
	if (not ctx->connected()) {
		delete ctx, ctx = nullptr;
	}
	return ctx;
}

STATIC_CONSTRUCTOR(_egl_init) {
	platform::add_driver(_create_egl_context, 1);
}

eglcontext::eglcontext(display* display)
	: gfxcontext(display, "EGL", this) {

	// Get the display format
	const display::pixel_format& fmt = _display->get_color_format();

	// Currently empty
	EGLint context_attrs[] = {
		EGL_NONE
	};

	// Configure the depth and rgb bit-depth
	EGLint config_attrs[] = {
		EGL_SURFACE_TYPE,
		EGL_WINDOW_BIT,
		EGL_DEPTH_SIZE, fmt.depth,
		EGL_RED_SIZE, fmt.r,
		EGL_RED_SIZE, fmt.g,
		EGL_RED_SIZE, fmt.b,

		EGL_RENDERABLE_TYPE,
		EGL_OPENGL_BIT, // 7
		EGL_NONE
	};

	// Attempt to retrieve an egl display from the native display
	_egldisplay = eglGetDisplay(_display->get_native_handle());
	if (_egldisplay == nullptr) {
		cleanup();
		return;
	}

	// Attempt to initialize egl
	if (not eglInitialize(_egldisplay, nullptr, nullptr)) {
		cleanup();
		return;
	}

	// Attempt to bind to Core OpenGL, otherwise OpenGL ES
	if (not eglBindAPI(EGL_OPENGL_API)) {
		if (not eglBindAPI(EGL_OPENGL_ES_API)) {
			cleanup();
			return;
		}
		config_attrs[7] = EGL_OPENGL_ES_BIT; // Change the support bit to OpenGL ES
	}

	// Select a configuration
	EGLint n;
	if (not eglChooseConfig(_egldisplay, config_attrs, &_eglconfig, 1, &n)) {
		cleanup();
		return;
	}

	// Create the context
	_eglcontext = eglCreateContext(_egldisplay, _eglconfig, EGL_NO_CONTEXT, context_attrs);
	if (_eglcontext == nullptr) {
		cleanup();
		return;
	}

	// make this the current context so we can retrieve the information below
	eglMakeCurrent(_egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, _eglcontext);

	// Query available extensions
	const char* ptr = eglQueryString(_egldisplay, EGL_EXTENSIONS);
	_extensions = { ptr, strlen(ptr) + 1 };

	// Query the context and version
	eglQueryContext(_egldisplay, _eglcontext, EGL_CONTEXT_CLIENT_TYPE, &_eglctype);
	glGetIntegerv(GL_MAJOR_VERSION, &_eglvmajor);
	glGetIntegerv(GL_MINOR_VERSION, &_eglvminor);
}

eglcontext::~eglcontext() {
	cleanup();
}

bool eglcontext::connected() {
	return _eglcontext != nullptr;
}

const cstring& eglcontext::get_context_name() {
	static constexpr cstring opengl = "OpenGL";
	static constexpr cstring gles   = "GLES";
	static constexpr cstring openvg = "OpenVG"; // this should never be used
	switch (_eglctype) {
		default:
		case EGL_OPENGL_API:    return opengl;
		case EGL_OPENGL_ES_API: return gles;
		case EGL_OPENVG_API:    return openvg;
	}
}

bool eglcontext::check_extension(const cstring& ext) {
	return _extensions.find(ext) != _extensions.size();
}

void eglcontext::make_current(gfxsurface*) {

}

void eglcontext::cleanup() {
	eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
	eglDestroyContext(_display, _eglcontext);
	eglTerminate(_display);
	eglReleaseThread();

	_egldisplay = nullptr;
	_eglconfig  = nullptr;
	_eglcontext = nullptr;
	_eglvmajor  = 0;
	_eglvminor  = 0;
}

}
