#include "GEScene.h"

#include "GECube.h"
#include "GEPyramid.h"
#include "GECone.h"
#include "GECylinder.h"
#include "GESphere.h"
#include "GEDisk.h"
#include "GETorus.h"
#include "GEIcosahedron.h"
#include "GETransform.h"
#include <windows.h>
#include "resource.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

//
// FUNCIÓN: GEScene::GEScene(GEGraphicsContext* gc, GEDrawingContext* dc)
//
// PROPÓSITO: Crea la escena
//
GEScene::GEScene(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc)
{
	VkExtent2D extent = dc->getExtent();
	double aspect = (double)extent.width / (double)extent.height;
	aspect_ratio(aspect);

	GEPipelineConfig* config = createPipelineConfig(dc->getExtent());
	rc = new GERenderingContext(gc, dc, config);

	figure[0] = new GECube(10.0f);
	figure[0]->initialize(gc, rc);

	figure[1] = new GEPyramid(10.0f);
	figure[1]->initialize(gc, rc);

	figure[2] = new GECone(5, 20, 10.0f, 10.0f);
	figure[2]->initialize(gc, rc);

	figure[3] = new GECylinder(20, 20, 10.0f, 10.0f);
	figure[3]->initialize(gc, rc);

	figure[4] = new GESphere(20, 20, 10.0f);
	figure[4]->initialize(gc, rc);

	figure[5] = new GEDisk(5, 20, 7.0f, 15.0f);
	figure[5]->initialize(gc, rc);

	figure[6] = new GETorus(10, 20, 5.0f, 10.0f);
	figure[6]->initialize(gc, rc);

	figure[7] = new GEIcosahedron(10.0f);
	figure[7]->initialize(gc, rc);

	fillCommandBuffers(cc->commandBuffers);

	model = glm::mat4(1.0f);
	view = glm::mat4(1.0f);
	view = glm::translate(view, glm::vec3(0.0f, 0.0f, -100.0f));
	figureIndex = 0;
}

//
// FUNCIÓN: GEScene::destroy(GEGraphicsContext* gc)
//
// PROPÓSITO: Reconstruye los componentes gráficos de la escena
//
void GEScene::destroy(GEGraphicsContext* gc)
{
	rc->destroy(gc);
	delete rc;

	for (int i = 0; i < 3; i++)
	{
		figure[i]->destroy(gc); 
		delete figure[i];
	}

}

//
// FUNCIÓN: GEScene::recreate(GEGraphicsContext* gc, GEDrawingContext* dc)
//
// PROPÓSITO: Reconstruye los componentes gráficos de la escena
//
void GEScene::recreate(GEGraphicsContext* gc, GEDrawingContext* dc, GECommandContext* cc)
{
	rc->destroy(gc);
	GEPipelineConfig* config = createPipelineConfig(dc->getExtent());
	this->rc = new GERenderingContext(gc, dc, config);
	fillCommandBuffers(cc->commandBuffers);
}

//
// FUNCIÓN: GEScene::update(GEGraphicsContext* gc, uint32_t index)
//
// PROPÓSITO: Actualiza la información para generar la imagen 
//
void GEScene::update(GEGraphicsContext* gc, uint32_t index)
{
	GETransform ubo = {};
	ubo.MVP = projection * view * model;

	figure[figureIndex]->update(gc, index, ubo);
}

//
// FUNCIÓN: GEScene::key_pressed(int)
//
// PROPÓSITO: Respuesta a acciones de teclado
//
void GEScene::key_pressed(int key)
{
	switch (key)
	{
	case GLFW_KEY_UP:
		model = glm::rotate(model, glm::radians(5.0f), glm::vec3(1.0f, 0.0f, 0.0f));
		break;
	case GLFW_KEY_DOWN:
		model = glm::rotate(model, glm::radians(-5.0f), glm::vec3(1.0f, 0.0f, 0.0f));
		break;
	case GLFW_KEY_LEFT:
		model = glm::rotate(model, glm::radians(5.0f), glm::vec3(0.0f, 1.0f, 0.0f));
		break;
	case GLFW_KEY_RIGHT:
		model = glm::rotate(model, glm::radians(-5.0f), glm::vec3(0.0f, 1.0f, 0.0f));
		break;
	case GLFW_KEY_F:
		figureIndex = (figureIndex + 1) % 8;
		break;
	}
}

//
// FUNCIÓN: GEScene::aspect_ratio(double)
//
// PROPÓSITO: Modifica la relación anchura/altura del modelo
//
void GEScene::aspect_ratio(double aspect)
{
	constexpr double fov = glm::radians(30.0f);
	double sin_fov = sin(fov);
	double cos_fov = cos(fov);
	float wHeight = (float)(sin_fov * 0.2 / cos_fov);
	float wWidth = (float)(wHeight * aspect);

	projection = glm::perspective((float)fov, (float)aspect, 0.2f, 400.0f);
	projection[1][1] *= -1.0f;
}

//
// FUNCIÓN: CAScene::fillCommandBuffers(std::vector<VkCommandBuffer> commandBuffers)
//
// PROPÓSITO: Añade los comandos de renderizado al command buffer
//
void GEScene::fillCommandBuffers(std::vector<VkCommandBuffer> commandBuffers)
{
	rc->startFillingCommandBuffers(commandBuffers);
	for (int i = 0; i < commandBuffers.size(); i++) figure[figureIndex]->addCommands(commandBuffers[i], rc->pipelineLayout, i);
	rc->endFillingCommandBuffers(commandBuffers);
}

//
// FUNCIÓN: GEScene::createPipelineConfig()
//
// PROPÓSITO: Obtiene la configuración del pipeline de renderizado
//
GEPipelineConfig* GEScene::createPipelineConfig(VkExtent2D extent)
{
	GEPipelineConfig* config = new GEPipelineConfig();
	config->vertex_shader = IDR_HTML1;
	config->fragment_shader = IDR_HTML2;

	config->attrStride = sizeof(GEVertex);
	config->attrOffsets.resize(1);
	config->attrOffsets[0] = offsetof(GEVertex, pos);
	config->attrFormats.resize(1);
	config->attrFormats[0] = VK_FORMAT_R32G32B32_SFLOAT;

	config->descriptorTypes.resize(1);
	config->descriptorTypes[0] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
	config->descriptorStages.resize(1);
	config->descriptorStages[0] = VK_SHADER_STAGE_VERTEX_BIT;

	config->depthTestEnable = VK_TRUE;
	config->cullMode = VK_CULL_MODE_BACK_BIT;
	config->extent = extent;

	return config;
}