#include "demo.h"

GLuint create_texture_from_image(int width, int height, uint8_t *image, GLenum format, GLenum repeat)
{
	GLuint texture_id;

	glGenTextures (1, &texture_id);
	glBindTexture (GL_TEXTURE_2D, texture_id);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, image);
	glGenerateMipmap(GL_TEXTURE_2D);

        return texture_id;
}

GLuint create_texture_from_file(char *name, GLenum repeat)
{
	int width, height;
	unsigned char* image;
	GLuint texture_id;
		    
	image = SOIL_load_image(name, &width, &height, 0, SOIL_LOAD_RGB);
	if (!image) {
		fprintf(stderr,"failed to load %s\n", name);
		exit(1);
	}

	texture_id = create_texture_from_image(width, height, image, GL_RGB, repeat);

	SOIL_free_image_data(image);

	return texture_id;
}

cairo_t* create_cairo_context(int width, int height, int channels,
                      cairo_surface_t** surf, unsigned char** buffer)
{
    *buffer = calloc (channels * width * height, sizeof (**buffer));
    *surf = cairo_image_surface_create_for_data (*buffer, CAIRO_FORMAT_ARGB32, width,
                                                 height, channels * width);
    return cairo_create (*surf);
}

cairo_t* create_layout_context(void)
{
    cairo_surface_t *temp_surface;
    cairo_t *context;

    temp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
    context = cairo_create (temp_surface);
    cairo_surface_destroy (temp_surface);

    return context;
}

void get_text_size (PangoLayout *layout, int *width, int *height)
{
    pango_layout_get_size (layout, width, height);
    /* Divide by pango scale to get dimensions in pixels. */
    *width /= PANGO_SCALE;
    *height /= PANGO_SCALE;
}

GLuint render_text (const char *text, int *text_width, int *text_height, char *font, GLenum repeat)
{
    cairo_t *layout_context;
    cairo_t *render_context;
    cairo_surface_t *temp_surface;
    cairo_surface_t *surface;
    unsigned char* surface_data = NULL;
    PangoFontDescription *desc;
    PangoLayout *layout;
    GLuint texture_id;

    layout_context = create_layout_context ();

    /* Create a PangoLayout, set the font and text */
    layout = pango_cairo_create_layout (layout_context);
    pango_layout_set_text (layout, text, -1);

    /* Load the font */
    desc = pango_font_description_from_string (font);
    pango_layout_set_font_description (layout, desc);
    pango_font_description_free (desc);

    /* Get text dimensions and create a context to render to */
    get_text_size (layout, text_width, text_height);
    render_context = create_cairo_context (*text_width, *text_height, 4, &surface, &surface_data);

    /* Render */
    cairo_set_source_rgba (render_context, 1, 1, 1, 1);
    pango_cairo_show_layout (render_context, layout);

   // texture_id = create_texture_from_image(*text_width, *text_height, surface_data, GL_RGBA, GL_CLAMP_TO_EDGE);
    texture_id = create_texture_from_image(*text_width, *text_height, surface_data, GL_RGBA, repeat);

    /* Clean up */
    free (surface_data);
    g_object_unref (layout);
    cairo_destroy (layout_context);
    cairo_destroy (render_context);
    cairo_surface_destroy (surface);

    return texture_id;
}

static int compile_shader(GLuint shader)
{
	GLint status;

	glCompileShader(shader);
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (!status) {
		char buffer[512];

		glGetShaderInfoLog(shader, 512, NULL, buffer);
		fprintf(stderr, "shader compile failed: %s\n", buffer);

		return -1;
	}

	return 0;
}

int setup_shaders(struct shaderProgram *p)
{
	GLint status;

	if (p->vertexShadersrc) {
		p->vertexShader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(p->vertexShader, 1, p->vertexShadersrc, p->vertexShadersize);
		if (compile_shader(p->vertexShader) < 0) {
			fprintf(stderr, "compile failed for vertex shader %s\n", p->name);
			return -1;
		}
	}
	p->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(p->fragmentShader, 1, p->fragmentShadersrc, p->fragmentShadersize);
	if (compile_shader(p->fragmentShader) < 0) {
		fprintf(stderr, "compile failed for fragment shader %s\n", p->name);
		return -1;
	}
	p->shaderProgram = glCreateProgram();
	if (p->vertexShadersrc)
		glAttachShader(p->shaderProgram, p->vertexShader);
	glAttachShader(p->shaderProgram, p->fragmentShader);
	glBindFragDataLocation(p->shaderProgram, 0, "outColor");

	glLinkProgram(p->shaderProgram);
	glGetProgramiv(p->shaderProgram, GL_LINK_STATUS, &status);
	if (!status) {
		char buffer[512];

		glGetProgramInfoLog(p->shaderProgram, 512, NULL, buffer);
		fprintf(stderr, "shader link failed: %s\n", buffer);

		return -1;
	}

	return 0;
}

void delete_shaders(struct shaderProgram *p) {
	glDeleteProgram(p->shaderProgram);
	glDeleteShader(p->fragmentShader);
	glDeleteShader(p->vertexShader);
}

void setup_attributes(struct shaderProgram *p, struct attribute *attrs, int size, int stride)
{
	int i;

	for (i = 0; i < size; i++) {
		GLint attrib;

		attrib = glGetAttribLocation(p->shaderProgram, attrs[i].name);
		glEnableVertexAttribArray(attrib);
		glVertexAttribPointer(attrib, attrs[i].size, GL_FLOAT, GL_FALSE, stride * sizeof(GLfloat), (void *)(attrs[i].offset * sizeof(GLfloat)));
	}
}

void setup_vertices(GLuint *vao, GLuint *vbo, GLfloat *vertices, GLint size)
{
	glGenVertexArrays(1, vao);
	glBindVertexArray(*vao);

	glGenBuffers(1, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, *vbo);
	glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
}

