#include "framebuffer.h"
#include "error.h"
#include "gl.h"
#include "shader.h"
#include "shaders.h"
#include <stdlib.h>

static f32 screen_rect[12] = {
    -1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f};

u32 sw_screen_vao;
static u32 vbo;

struct sw_framebuffer *sw_framebuffer_create(struct sw_vec2i size) {
	struct sw_framebuffer *fb;

	sw_log("init framebuffer");
	fb = malloc(sizeof(struct sw_framebuffer));
	glGenFramebuffers(1, &fb->_fb);
	glGenTextures(1, &fb->_fbtex);
	glGenTextures(1, &fb->_fbdepth);
	sw_framebuffer_resize(fb, size);

	return fb;
}

void sw_framebuffer_destroy(struct sw_framebuffer *fb) {
	glDeleteTextures(1, &fb->_fbtex);
	glDeleteFramebuffers(1, &fb->_fb);
}

void sw_framebuffer_static_init() {
	sw_log("init static framebuffer vao");
	glGenVertexArrays(1, &sw_screen_vao);
	glBindVertexArray(sw_screen_vao);

	sw_log("init static framebuffer vbo");
	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(
	    GL_ARRAY_BUFFER, sizeof(f32) * 12, screen_rect, GL_STATIC_DRAW
	);
	glVertexAttribPointer(SW_SHADERLOC_POS, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glEnableVertexAttribArray(SW_SHADERLOC_POS);

	glBindVertexArray(0);
	sw_log("init static framebuffer done");
}

void sw_framebuffer_copy_to_screen(struct sw_framebuffer *fb) {
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	sw_shaderprogram_use(sw_shaders_copy);
	sw_framebuffer_render(fb);
}

void sw_framebuffer_render(struct sw_framebuffer *fb) {
	glBindVertexArray(sw_screen_vao);
	if(fb) {
		glBindTexture(GL_TEXTURE_2D, fb->_fbtex);
	}

	glClear(GL_DEPTH_BUFFER_BIT);

	glDrawArrays(GL_TRIANGLES, 0, 6);
	glBindVertexArray(0);
}

void sw_framebuffer_resize(
    struct sw_framebuffer *fb, struct sw_vec2i new_size
) {
	sw_log("resize framebuffer to %ix%i", new_size.x, new_size.y);
	fb->size = new_size;
	glBindFramebuffer(GL_FRAMEBUFFER, fb->_fb);
	glBindTexture(GL_TEXTURE_2D, fb->_fbtex);
	glTexImage2D(
	    GL_TEXTURE_2D,
	    0,
	    GL_RGBA8,
	    new_size.x,
	    new_size.y,
	    0,
	    GL_RGBA,
	    GL_UNSIGNED_BYTE,
	    0
	);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glFramebufferTexture2D(
	    GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->_fbtex, 0
	);

	glBindTexture(GL_TEXTURE_2D, fb->_fbdepth);
	glTexImage2D(
	    GL_TEXTURE_2D,
	    0,
	    GL_DEPTH_COMPONENT16,
	    new_size.x,
	    new_size.y,
	    0,
	    GL_DEPTH_COMPONENT,
	    GL_UNSIGNED_SHORT,
	    0
	);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glFramebufferTexture2D(
	    GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb->_fbdepth, 0
	);
}
