#ifndef SIMPLE_RENDER_H
#define SIMPLE_RENDER_H

#include <stdint.h>

#include <SDL2/SDL.h>
#define GLEW_STATIC
#include <GL/glew.h>
#define GL_GLEXT_PROTOTYPES
#include <SDL2/SDL_opengl.h>

#include "ht.h"
#include "da.h"
#include "vec2.h"
#include "vec4.h"
#include "glyph_attr.h"

enum simple_vertex_attr {
	SIMPLE_VERTEX_ATTR_POS = 0,
	SIMPLE_VERTEX_ATTR_COLOR,
	SIMPLE_VERTEX_ATTR_UV,
	SIMPLE_VERTEX_ATTR_TOTAL,
};

struct simple_vertex {
	struct vec2f pos;
	struct vec4f color;
	struct vec2f uv;
};

struct simple_render {
	HT_DEF_STRUCT_ITEM(int32_t, uniforms);
	DA_DEF_STRUCT_ITEM(struct simple_vertex, vertexs);

	uint32_t vao;
	uint32_t vbo;
	struct {
		uint32_t vert;
		uint32_t frag;
	} shaders;
	uint32_t prog;
};

struct simple_render sr_create(const char *vert_path, const char *frag_path);
int32_t sr_set_uniform(struct simple_render *sr, const char *uniform_name);
int32_t sr_get_uniform(const struct simple_render *sr,
		       const char *uniform_name);
void sr_use(const struct simple_render *sr);
void sr_triangle(struct simple_render *sr,
		 struct vec2f p0, struct vec2f p1, struct vec2f p2,
		 struct vec4f c0, struct vec4f c1, struct vec4f c2,
		 struct vec2f uv0, struct vec2f uv1, struct vec2f uv2);
void sr_quad(struct simple_render *sr,
	     struct vec2f p0, struct vec2f p1, struct vec2f p2, struct vec2f p3,
	     struct vec4f c0, struct vec4f c1, struct vec4f c2, struct vec4f c3,
	     struct vec2f uv0, struct vec2f uv1,
	     struct vec2f uv2, struct vec2f uv3);
void sr_solid_rect(struct simple_render *sr, struct vec2f pos,
		   struct vec2f size, struct vec4f color);
void sr_draw(const struct simple_render *sr);

#if defined(SIMPLE_RENDER_IMP) || defined(IMP)

#include "shader.h"
#include "gl_util.h"

#include "util_macro.h"

struct simple_render
sr_create(const char *vert_path, const char *frag_path)
{
	struct simple_render sr = {0};
	HT_CREATE(sr.uniforms, 32);
	DA_CREATE(sr.vertexs);

	/* sr.vertexs.cap = 1024 * 1024; */
	/* sr.vertexs.size = 0; */
	/* sr.vertexs.items = calloc(sr.vertexs.cap, sizeof(struct simple_vertex)); */

	{
		glGenVertexArrays(1, &sr.vao);
		glBindVertexArray(sr.vao);

		glGenBuffers(1, &sr.vbo);
		glBindBuffer(GL_ARRAY_BUFFER, sr.vbo);

		/* glBufferData(GL_ARRAY_BUFFER, */
		/* 	     (ssize_t) (sizeof(struct simple_vertex) */
		/* 			* sr.vertexs.cap), */
		/* 	     sr.vertexs.items, */
		/* 	     GL_DYNAMIC_DRAW); */
#if 0
		static const struct glyph_attr
			simple_vertex_attr[SIMPLE_VERTEX_ATTR_TOTAL] = {
			[SIMPLE_VERTEX_ATTR_POS] = {
				.size = 2,
				.type = GL_FLOAT,
				.norm = GL_FALSE,
				.stride = sizeof(struct simple_vertex),
				.offset = offsetof(struct simple_vertex,
						   pos)
			},
			[SIMPLE_VERTEX_ATTR_COLOR] = {
				.size = 4,
				.type = GL_FLOAT,
				.norm = GL_FALSE,
				.stride = sizeof(struct simple_vertex),
				.offset = offsetof(struct simple_vertex,
						   color)
			},
			[SIMPLE_VERTEX_ATTR_UV] = {
				.size = 2,
				.type = GL_FLOAT,
				.norm = GL_FALSE,
				.stride = sizeof(struct simple_vertex),
				.offset = offsetof(struct simple_vertex,
						   uv)
			}
		};
		glyph_attr_declare(simple_vertex_attr,
				   SIMPLE_VERTEX_ATTR_TOTAL);
#else
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
				      sizeof(struct simple_vertex),
				      (void *) offsetof(struct simple_vertex,
							pos));
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE,
				      sizeof(struct simple_vertex),
				      (void *) offsetof(struct simple_vertex,
							color));
		glEnableVertexAttribArray(2);
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,
				      sizeof(struct simple_vertex),
				      (void *) offsetof(struct simple_vertex,
							uv));
#endif
	}

	sr.shaders.vert = shader_compile_file_or_exit(
		vert_path, GL_VERTEX_SHADER);
	sr.shaders.frag = shader_compile_file_or_exit(
		frag_path, GL_FRAGMENT_SHADER);
	sr.prog = program_link_or_exit(sr.shaders.vert, sr.shaders.frag);

	glUseProgram(sr.prog);

	sr_set_uniform(&sr, "resolution");
	sr_set_uniform(&sr, "time");
	sr_set_uniform(&sr, "camera");
	return sr;
}

int32_t
sr_set_uniform(struct simple_render *sr, const char *uniform_name)
{
	int32_t uni = get_uniform(sr->prog, uniform_name);
	printf("%s: Got Uniform %s -> %d\n", __func__, uniform_name, uni);
	HT_SET(sr->uniforms, uniform_name, strlen(uniform_name), uni);
	return uni;
}
int32_t
sr_get_uniform(const struct simple_render *sr, const char *uniform_name)
{
	int32_t uni = -1;
	HT_GET(sr->uniforms, uniform_name, strlen(uniform_name), uni);
	return uni;
}

void
sr_use(const struct simple_render *sr)
{
	glBindVertexArray(sr->vao);
	glBindBuffer(GL_ARRAY_BUFFER, sr->vbo);
	glUseProgram(sr->prog);
}

void
sr_triangle(struct simple_render *sr,
	    struct vec2f p0, struct vec2f p1, struct vec2f p2,
	    struct vec4f c0, struct vec4f c1, struct vec4f c2,
	    struct vec2f uv0, struct vec2f uv1, struct vec2f uv2)
{
	struct simple_vertex sv0 = {p0, c0, uv0};
	struct simple_vertex sv1 = {p1, c1, uv1};
	struct simple_vertex sv2 = {p2, c2, uv2};
	DA_APPEND(sr->vertexs, sv0);
	DA_APPEND(sr->vertexs, sv1);
	DA_APPEND(sr->vertexs, sv2);
}

void
sr_quad(struct simple_render *sr,
	struct vec2f p0, struct vec2f p1, struct vec2f p2, struct vec2f p3,
	struct vec4f c0, struct vec4f c1, struct vec4f c2, struct vec4f c3,
	struct vec2f uv0, struct vec2f uv1, struct vec2f uv2, struct vec2f uv3)
{
	sr_triangle(sr, p0, p1, p2, c0, c1, c2, uv0, uv1, uv2);
	sr_triangle(sr, p1, p2, p3, c1, c2, c3, uv1, uv2, uv3);
}

void
sr_solid_rect(struct simple_render *sr, struct vec2f pos, struct vec2f size,
	      struct vec4f color)
{
	struct vec2f uv = vec2fs(0);
	sr_quad(sr,
		pos, vec2f_add(pos, vec2f(size.x, 0)),
		vec2f_add(pos, vec2f(0, size.y)), vec2f_add(pos, size),
		X4_ARGS(color),
		X4_ARGS(uv));
}

void
sr_draw(const struct simple_render *sr)
{
	glBufferData(GL_ARRAY_BUFFER,
		     (ssize_t) (sizeof(struct simple_vertex)
				* sr->vertexs.cap),
		     sr->vertexs.items,
		     GL_DYNAMIC_DRAW);
	glDrawArrays(GL_TRIANGLES, 0, (int32_t) sr->vertexs.size);
}


#endif /* defined(SIMPLE_RENDER_IMP) || defined(IMP) */

#endif
