#pragma once
#include <gl/Framebuffer.h>

#include "Engine.h"

enum class LightType {
	DIRECTIONAL = 0,
	POINT = 1,
};

struct LightDesc {
	uint32_t shadowmap_x = 1024;
	uint32_t shadowmap_y = 1024;
	LightType type = LightType::POINT;
	float intensity = 1.0;
	float attenuation = 1.0;
	glm::vec3 colour = glm::vec3(1);
	glm::vec3 pos;
	glm::vec3 target;
	glm::vec3 dir;
};

class Light {
public:
	LightType type = LightType::DIRECTIONAL;

	Framebuffer* fb;
	int generation = 0;

	float intensity = 1.0;
	glm::vec3 colour;
	glm::vec3 target;
	glm::vec3 pos;
	glm::vec3 dir;

	float attenuation = 0.0;

	float near_plane =0.2f;
	float far_plane = 45.0f;
	float radius = 1.0;

	int alloc_slot;

	glm::mat4 view_proj_mat;
	glm::mat4 proj_mat;
	glm::mat4 view_mat;


	// glm::mat4 proj_mats_pointlight[6];

	glm::mat4 view_mats_pointlight[6];



	Light( LightDesc light_desc ):
		pos(light_desc.pos),
		target(light_desc.target),
		type(light_desc.type),
		dir(light_desc.dir),
		colour(light_desc.colour),
		attenuation(light_desc.attenuation),
		intensity(light_desc.intensity){
		if(light_desc.type == LightType::POINT) {
			fb = new Framebuffer({
				.depth_texture = WEngine->alloc_textures.push(TextureDesc{
					.resx = light_desc.shadowmap_x,
					.resy = light_desc.shadowmap_y,
					.resz = 1,
					.internal_format = InternalFormat::DEPTH_COMPONENT24,
					// .format = Format::DEPTH_COMPONENT,
					// .type = Type::UNSIGNED_INT,
					.is_cubemap = true
				}).ptr,
				.layered = true,
				.name = "Light FB"
			});
		} else {
			fb = new Framebuffer({WArray<Texture*>({ }), false, false,
				WEngine->alloc_textures.push(TextureDesc{ light_desc.shadowmap_x, light_desc.shadowmap_y, 1, InternalFormat::DEPTH_COMPONENT24 }).ptr,
				false,
				"Light FB"
			});
		}
		// fb->name = std::string("Light FB");
	}
	Light() = default;

	inline void calc_matrices() {
		if(this->type == LightType::DIRECTIONAL) {
			float range = 15.0f;
			proj_mat = glm::ortho(-range, range, -range, range, near_plane, far_plane);
			this->pos = this->target;
			this->pos += this->dir * 20.0f;
			view_mat = glm::lookAt(this->pos, this->target, glm::vec3( 0.0f, 1.0f,  0.0f));
			view_proj_mat = proj_mat * view_mat;
		} else if(this->type == LightType::POINT) {
			float aspect_ratio = 1.0f;
			this->proj_mat = glm::perspective(glm::radians(90.0f), aspect_ratio, near_plane, far_plane);
			std::array<glm::mat4, 6> shadowTransforms = {
				 glm::lookAt(this->pos, this->pos + glm::vec3( 1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)) ,
				 glm::lookAt(this->pos, this->pos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)),
				 glm::lookAt(this->pos, this->pos + glm::vec3( 0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0)),
				 glm::lookAt(this->pos, this->pos + glm::vec3( 0.0,-1.0, 0.0), glm::vec3(0.0, 0.0,-1.0)),
				 glm::lookAt(this->pos, this->pos + glm::vec3( 0.0, 0.0, 1.0), glm::vec3(0.0,-1.0, 0.0)),
				 glm::lookAt(this->pos, this->pos + glm::vec3( 0.0, 0.0,-1.0), glm::vec3(0.0,-1.0, 0.0))
			};

			int i = 0;
			for(glm::mat4 trans : shadowTransforms) {
				this->view_mats_pointlight[i] = trans;
				i++;
			}
		}
	}

};
