/* ResidualVM - A 3D game interpreter
 *
 * ResidualVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "graphics/opengl/context.h"

#include "common/debug.h"
#include "common/str.h"
#include "common/textconsole.h"
#include "common/tokenizer.h"

#include "graphics/opengl/system_headers.h"

#ifdef USE_OPENGL

namespace Common {
DECLARE_SINGLETON(OpenGL::Context);
}

namespace OpenGL {

Context::Context() {
	reset();
}

void Context::reset() {
	maxTextureSize = 0;

	NPOTSupported = false;
	shadersSupported = false;
	framebufferObjectSupported = false;
	packedDepthStencilSupported = false;
	unpackSubImageSupported = false;
	framebufferObjectMultisampleSupported = false;
	multisampleMaxSamples = -1;
}

void Context::initialize(ContextType contextType) {
	// Initialize default state.
	reset();

	type = contextType;

	// Obtain maximum texture size.
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&maxTextureSize);
	debug(5, "OpenGL maximum texture size: %d", maxTextureSize);

	const char *extString = (const char *)glGetString(GL_EXTENSIONS);

	bool ARBShaderObjects = false;
	bool ARBShadingLanguage100 = false;
	bool ARBVertexShader = false;
	bool ARBFragmentShader = false;
	bool EXTFramebufferMultisample = false;
	bool EXTFramebufferBlit = false;

	Common::StringTokenizer tokenizer(extString, " ");
	while (!tokenizer.empty()) {
		Common::String token = tokenizer.nextToken();

		if (token == "GL_ARB_texture_non_power_of_two" || token == "GL_OES_texture_npot") {
			NPOTSupported = true;
		} else if (token == "GL_ARB_shader_objects") {
			ARBShaderObjects = true;
		} else if (token == "GL_ARB_shading_language_100") {
			ARBShadingLanguage100 = true;
		} else if (token == "GL_ARB_vertex_shader") {
			ARBVertexShader = true;
		} else if (token == "GL_ARB_fragment_shader") {
			ARBFragmentShader = true;
		} else if (token == "GL_EXT_framebuffer_object") {
			framebufferObjectSupported = true;
		} else if (token == "GL_EXT_packed_depth_stencil" || token == "GL_OES_packed_depth_stencil") {
			packedDepthStencilSupported = true;
		} else if (token == "GL_EXT_unpack_subimage") {
			unpackSubImageSupported = true;
		} else if (token == "GL_EXT_framebuffer_multisample") {
			EXTFramebufferMultisample = true;
		} else if (token == "GL_EXT_framebuffer_blit") {
			EXTFramebufferBlit = true;
		}
	}

	int glslVersion = getGLSLVersion();
	debug(5, "OpenGL GLSL version: %d", glslVersion);

	if (type == kContextGLES2) {
		// GLES2 always has (limited) NPOT support.
		NPOTSupported = true;

		// GLES2 always has shader support.
		shadersSupported = true;

		// GLES2 always has FBO support.
		framebufferObjectSupported = true;

		// ResidualVM does not support multisample FBOs with GLES2 for now
		framebufferObjectMultisampleSupported = false;
		multisampleMaxSamples = -1;
	} else {
		shadersSupported = ARBShaderObjects && ARBShadingLanguage100 && ARBVertexShader && ARBFragmentShader && glslVersion >= 120;

		// Desktop GL always has unpack sub-image support
		unpackSubImageSupported = true;

		framebufferObjectMultisampleSupported = EXTFramebufferMultisample && EXTFramebufferBlit;

		if (framebufferObjectMultisampleSupported) {
			glGetIntegerv(GL_MAX_SAMPLES, (GLint *)&multisampleMaxSamples);
		}
	}

	// Log context type.
	switch (type) {
		case kContextGL:
			debug(5, "OpenGL: GL context initialized");
			break;

		case kContextGLES2:
			debug(5, "OpenGL: GLES2 context initialized");
			break;
	}

	// Log features supported by GL context.
	debug(5, "OpenGL: NPOT texture support: %d", NPOTSupported);
	debug(5, "OpenGL: Shader support: %d", shadersSupported);
	debug(5, "OpenGL: FBO support: %d", framebufferObjectSupported);
	debug(5, "OpenGL: Packed depth stencil support: %d", packedDepthStencilSupported);
	debug(5, "OpenGL: Unpack subimage support: %d", unpackSubImageSupported);
}

int Context::getGLSLVersion() const {
	const char *glslVersionString = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
	if (!glslVersionString) {
		warning("Could not get GLSL version");
		return 0;
	}

	const char *glslVersionFormat;
	if (type == kContextGL) {
		glslVersionFormat = "%d.%d";
	} else {
		glslVersionFormat = "OpenGL ES GLSL ES %d.%d";
	}

	int glslMajorVersion, glslMinorVersion;
	if (sscanf(glslVersionString, glslVersionFormat, &glslMajorVersion, &glslMinorVersion) != 2) {
		warning("Could not parse GLSL version '%s'", glslVersionString);
		return 0;
	}

	return glslMajorVersion * 100 + glslMinorVersion;
}

} // End of namespace OpenGL

#endif
