//object encapsulating an omni directional light source

#include "odsm_source.h"
#include <glm/gtx/transform.hpp>
// glm::perspective
#include <glm/gtc/matrix_projection.hpp>
// glm::translate, glm::rotate, glm::scale
#include <glm/gtc/matrix_transform.hpp>
// glm::value_ptr
#include <glm/gtc/type_ptr.hpp> 

odsm_source::odsm_source()
{
	cubemap_dim = 800;
}

odsm_source::odsm_source(const GLuint cm_dim, const glm::vec3 light, const glm::vec2 near_far)
{
	cubemap_dim = cm_dim;
	this->light = light;
	this->near_far = near_far;
}

odsm_source::~odsm_source()
{
	glDeleteFramebuffers(1, &odsm_FBO);

	glDeleteTextures(1, &odsm_tex_ID);
}

void odsm_source::init_odsm()
{
	//create FBO
	glGenFramebuffers(1, &odsm_FBO);
	glBindFramebuffer(GL_FRAMEBUFFER, odsm_FBO);

		// generate cubemap texture
	glGenTextures(1, &odsm_tex_ID);
	glBindTexture(GL_TEXTURE_CUBE_MAP, odsm_tex_ID);

	//fix seam-artifacts due to numerical precision limitations
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	//unsigned 24bit int z-buffer
	//if more precision is needed go GL_DEPTH_COMPONENT32F
	GLint internal_format = GL_DEPTH_COMPONENT24;
	GLenum data_type = GL_UNSIGNED_INT;

	GLenum format = GL_DEPTH_COMPONENT;

	for (GLint face=0; face < 6; face++) {
		glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
			0,
			internal_format,
			cubemap_dim, cubemap_dim, 0,
			format,
			data_type,
			NULL // dont need to specify content
			);
	}

	//attach cubemap at FBO's depth attachment point
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, odsm_tex_ID, 0);

	//tell opengl we did not add a color texture on purpose
	//else the FBO is considered incomplete
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);

	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		// ToDo:
		printf("GL_FRAMEBUFFER_COMPLETE error 0x%x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// then, just before rendering
	//glBindFramebuffer(GL_FRAMEBUFFER, odsm_FBO);

	//setup matrices
	perspective_odsm = glm::perspective(90.0f, 1.0f, 0.1f, near_far.y);
	//pos x
	cm_rots[0] = glm::mat4(glm::vec4(0.0f, 0.0f, -1.0f, 0.0f),
							glm::vec4(0.0f, -1.0f, 0.0f, 0.0f),
							glm::vec4(-1.0f, 0.0f, 0.0f, 0.0f),
							glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
	//neg x
	cm_rots[1] = glm::mat4(glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//pos y
	cm_rots[2] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, -1.0, 0.0),
							glm::vec4(0.0, 1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//neg y
	cm_rots[3] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//pos z
	cm_rots[4] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, -1.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//neg z
	cm_rots[5] = glm::mat4(glm::vec4(-1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));

	cm_trans = glm::translate(-light);
}
