// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <memory>

#include "Common/Assert.h"
#include "Common/GL/GLInterfaceBase.h"
#include "Common/GL/GLUtil.h"
#include "Common/Logging/Log.h"

std::unique_ptr<cInterfaceBase> GLInterface;
static GLuint attributelessVAO = 0;
static GLuint attributelessVBO = 0;

void InitInterface()
{
	GLInterface = HostGL_CreateGLInterface();
}

GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader)
{
	// generate objects
	GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
	GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
	GLuint programID = glCreateProgram();

	// compile vertex shader
	const char* shader = vertexShader.c_str();
	glShaderSource(vertexShaderID, 1, &shader, nullptr);
	glCompileShader(vertexShaderID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
	GLint Result = GL_FALSE;
	char stringBuffer[1024];
	GLsizei stringBufferUsage = 0;
	glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &Result);
	glGetShaderInfoLog(vertexShaderID, 1024, &stringBufferUsage, stringBuffer);

	if (Result && stringBufferUsage)
	{
		ERROR_LOG(VIDEO, "GLSL vertex shader warnings:\n%s%s", stringBuffer, vertexShader.c_str());
	}
	else if (!Result)
	{
		ERROR_LOG(VIDEO, "GLSL vertex shader error:\n%s%s", stringBuffer, vertexShader.c_str());
	}
	else
	{
		DEBUG_LOG(VIDEO, "GLSL vertex shader compiled:\n%s", vertexShader.c_str());
	}

	bool shader_errors = !Result;
#endif

	// compile fragment shader
	shader = fragmentShader.c_str();
	glShaderSource(fragmentShaderID, 1, &shader, nullptr);
	glCompileShader(fragmentShaderID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
	glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &Result);
	glGetShaderInfoLog(fragmentShaderID, 1024, &stringBufferUsage, stringBuffer);

	if (Result && stringBufferUsage)
	{
		ERROR_LOG(VIDEO, "GLSL fragment shader warnings:\n%s%s", stringBuffer, fragmentShader.c_str());
	}
	else if (!Result)
	{
		ERROR_LOG(VIDEO, "GLSL fragment shader error:\n%s%s", stringBuffer, fragmentShader.c_str());
	}
	else
	{
		DEBUG_LOG(VIDEO, "GLSL fragment shader compiled:\n%s", fragmentShader.c_str());
	}

	shader_errors |= !Result;
#endif

	// link them
	glAttachShader(programID, vertexShaderID);
	glAttachShader(programID, fragmentShaderID);
	glLinkProgram(programID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
	glGetProgramiv(programID, GL_LINK_STATUS, &Result);
	glGetProgramInfoLog(programID, 1024, &stringBufferUsage, stringBuffer);

	if (Result && stringBufferUsage)
	{
		ERROR_LOG(VIDEO, "GLSL linker warnings:\n%s%s%s", stringBuffer, vertexShader.c_str(), fragmentShader.c_str());
	}
	else if (!Result && !shader_errors)
	{
		ERROR_LOG(VIDEO, "GLSL linker error:\n%s%s%s", stringBuffer, vertexShader.c_str(), fragmentShader.c_str());
	}
#endif

	// cleanup
	glDeleteShader(vertexShaderID);
	glDeleteShader(fragmentShaderID);

	return programID;
}

void OpenGL_CreateAttributelessVAO()
{
	glGenVertexArrays(1, &attributelessVAO);
	_dbg_assert_msg_(VIDEO, attributelessVAO != 0, "Attributeless VAO should have been created successfully.")

	// In a compatibility context, we require a valid, bound array buffer.
	glGenBuffers(1, &attributelessVBO);
	_dbg_assert_msg_(VIDEO, attributelessVBO != 0, "Attributeless VBO should have been created successfully.")

	// Initialize the buffer with nothing.  16 floats is an arbitrary size that may work around driver issues.
	glBindBuffer(GL_ARRAY_BUFFER, attributelessVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, nullptr, GL_STATIC_DRAW);

	// We must also define vertex attribute 0.
	glBindVertexArray(attributelessVAO);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
	glEnableVertexAttribArray(0);
}

void OpenGL_BindAttributelessVAO()
{
	_dbg_assert_msg_(VIDEO, attributelessVAO != 0, "Attributeless VAO should have already been created.")
	glBindVertexArray(attributelessVAO);
}

void OpenGL_DeleteAttributelessVAO()
{
	_dbg_assert_msg_(VIDEO, attributelessVAO != 0, "Attributeless VAO should have already been created.")
	if (attributelessVAO != 0)
	{
		glDeleteVertexArrays(1, &attributelessVAO);
		glDeleteBuffers(1, &attributelessVBO);

		attributelessVAO = 0;
		attributelessVBO = 0;
	}
}
