#include <stdio.h>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include "vertexShader.c"
#include "fragmentShader.c"

#include <stdlib.h>
#include <math.h>
#include <string.h>

#define NUM_EDGES 20
#define RADIUS 0.7f
#define INNER_RADIUS 0.2f

typedef struct {
	GLfloat x;
	GLfloat y;
	GLfloat value;
} Point;

GLuint program;
GLuint vao;

void init(void) {

	// create and compile vertex shader
	GLchar *vertexText = malloc(vertexShader_glsl_len);
	memcpy(vertexText, vertexShader_glsl, vertexShader_glsl_len);
	const GLchar *vertexTextConst = vertexText;

	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexTextConst, &vertexShader_glsl_len);
	glCompileShader(vertexShader);

	GLint status;
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
	
	if (!status) {
		printf("Error compiling vertex shader: ");
		GLchar infoLog[1024];
		glGetShaderInfoLog(vertexShader, 1024, NULL, infoLog);
		printf("%s",infoLog);
	}

	// create and compile fragment shader
	
	GLchar *fragmentText = malloc(fragmentShader_glsl_len);
	memcpy(fragmentText, fragmentShader_glsl, fragmentShader_glsl_len);
	const GLchar *fragmentTextConst = fragmentText;

	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentTextConst, &fragmentShader_glsl_len);
	glCompileShader(fragmentShader);

	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
	
	if (!status) {
		printf("Error compiling fragment shader: ");
		GLchar infoLog[1024];
		glGetShaderInfoLog(fragmentShader, 1024, NULL, infoLog);
		printf("%s",infoLog);
	}

	// create and link shader program
	program = glCreateProgram();
	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);
	glLinkProgram(program);

	glGetProgramiv(program, GL_LINK_STATUS, &status);

	if (!status) {
		printf("Error linking program: ");
		GLchar infoLog[1024];
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		printf("%s",infoLog);
	}
	glValidateProgram(program);


	glGetProgramiv(program, GL_VALIDATE_STATUS, &status);

	if (!status) {
		printf("Error validating program: ");
		GLchar infoLog[1024];
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		printf("%s",infoLog);
	}


	size_t size = sizeof(Point) * (NUM_EDGES + 1) * 2;
	Point* triangleVertices = (Point *) malloc(size);

	float alpha;
	for (int i = 0; i <= NUM_EDGES; i++) {
		alpha = ((float) i) / NUM_EDGES * 3.14159f * 2;

		GLfloat dx = cos(alpha);
		GLfloat dy = sin(alpha);

		triangleVertices[2 * i].x = INNER_RADIUS * cos(alpha);
		triangleVertices[2 * i].y = INNER_RADIUS * sin(alpha);
		// triangleVertices[2 * i].value = ((float)i) / NUM_EDGES;
		triangleVertices[2 * i].value = 1.0f;

		triangleVertices[2 * i + 1].x = RADIUS * cos(alpha);
		triangleVertices[2 * i + 1].y = RADIUS * sin(alpha);
		// triangleVertices[2 * i + 1].value = ((float)i) / NUM_EDGES;
		triangleVertices[2 * i + 1].value = 0.0f;
	}

	GLuint triangleVertexBufferObject;
	glGenBuffers(1, &triangleVertexBufferObject);
	glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBufferObject);
	glBufferData(GL_ARRAY_BUFFER, size, triangleVertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);


	// create vertex array object
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBufferObject);
	glVertexAttribPointer(
		0,
		2,
		GL_FLOAT,
		GL_FALSE,
		sizeof(Point),
		0
	);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(
		1,
		1,
		GL_FLOAT,
		GL_FALSE,
		sizeof(Point),
		(GLvoid*)(2 * sizeof(GLfloat))
	);
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);



	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
}

void draw(void) {
	glClear(GL_COLOR_BUFFER_BIT);
	glUseProgram(program);
	glBindVertexArray(vao);

	glDrawArrays(GL_TRIANGLE_STRIP, 0, (NUM_EDGES + 1) * 2);
}

void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
	glViewport(0, 0, width, height);
}

int main(void) {
	printf("Hello World!\n");

	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow *window = glfwCreateWindow(800, 800, "Computergrafik 1", NULL, NULL);

	if (!window) {
		printf("Failed to create window\n");
		glfwTerminate();
		return -1;
	}


	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwMakeContextCurrent(window);

	glewInit();

	init();

	while (!glfwWindowShouldClose(window)) {
		draw();

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwTerminate();

	return 0;
}