#include "pch.h"
#include "TexturePool.h"
#include "gl/Buffer.h"


bool TexturePool::descriptors_are_the_same(TextureDesc& a, TextureDesc& b) {
	if(
		a.resx == b.resx && 
		a.resy == b.resy && 
		a.resz == b.resz && 
		a.is_cubemap == b.is_cubemap && 
		a.is_mipmapped == b.is_mipmapped && 
		a.mip_levels == b.mip_levels && 
		a.internal_format == b.internal_format && 
		a.tex_access == b.tex_access
	) {
		return true;
	} else {
		return false;
	}
}

TexturePool::TexturePool() {
	framebuffers.resize(100);
}

std::optional<TexturePool::TempTex*> TexturePool::find_temp_tex(TextureDesc& target_desc) {
	for(auto& proposal_tex : this->tex_pool) {
		bool found_match = descriptors_are_the_same(proposal_tex.tex.ptr->_texture_desc, target_desc);
		if( found_match ) {
			if(!proposal_tex.used_this_frame) {
				proposal_tex.used_this_frame = true;
				return &proposal_tex;
			}
		}
	}
	return std::nullopt;
}

TexturePool::TempTex* TexturePool::alloc_tex(TextureDesc& target_desc) {
	TempTex temp_tex_to_alloc;
	temp_tex_to_alloc.lifetime = 0;
	temp_tex_to_alloc.tex = WEngine->alloc_textures.push(target_desc);
	temp_tex_to_alloc.used_this_frame = true;

	for(TempFramebuffer& fb : this->framebuffers) {
		if(fb.free) {
			const bool is_first_init = fb.fb.pid == 1<<20;
			fb.fb.fb_desc.textures = WArray<Texture*>{temp_tex_to_alloc.tex.ptr};
			fb.fb.create();
			fb.free = false;
			temp_tex_to_alloc.fb = &fb;
			break;
		}
	}
			
	this->tex_pool.push_back(temp_tex_to_alloc);
	return &this->tex_pool[this->tex_pool.size()-1];
}

Texture* TexturePool::get_tex(TextureDesc target_desc) {
	// Framebuffer(FBDesc{})
	std::optional<TempTex*> found_tex = this->find_temp_tex(target_desc);
	if(found_tex.has_value()) {
		TempTex* proposal_tex = found_tex.value();
		proposal_tex->used_this_frame = true;
		return proposal_tex->tex.ptr;
	} else {
		TempTex* tex = alloc_tex(target_desc);
		return tex->tex.ptr;
	}
}

Framebuffer* TexturePool::get_framebuffer(TextureDesc target_desc) {
	// Framebuffer(FBDesc{})
	std::optional<TempTex*> found_tex = this->find_temp_tex(target_desc);
	if(found_tex.has_value()) {
		TempTex* proposal_tex = found_tex.value();
		proposal_tex->used_this_frame = true;
		return &proposal_tex->fb->fb;
	} else {
		TempTex* tex = alloc_tex(target_desc);
		return &tex->fb->fb;
	}
}

void TexturePool::end_frame() {
	tex_indices_to_remove.resize(0);
	int i = 0;
	for(auto& temp_tex : this->tex_pool) {
		if(temp_tex.used_this_frame) {
			temp_tex.lifetime = 0;
		} else {
			temp_tex.lifetime++;
		}
		temp_tex.used_this_frame = false;
		if(temp_tex.lifetime > 1000) {
			temp_tex.tex.ptr->destroy();
			glDeleteFramebuffers(1, &temp_tex.fb->fb.pid);
			temp_tex.fb->fb.pid = 1<<20;
			temp_tex.fb->free = true;
			WEngine->alloc_textures.pop(*temp_tex.tex.ptr);
			tex_indices_to_remove.push_back(i);
		}
		i++;
	}
	for (auto it = tex_indices_to_remove.rbegin(); it != tex_indices_to_remove.rend(); ++it) {
		tex_pool.erase(tex_pool.begin() + *it);
	}
}
