// Hello World in a shader.
// Kind of twisted, since it uses signed chars.
// New, ARB/EXT-free version. Not strict GL3+ yet though.

// Modern OpenGL. Only depends on MicroGlut for context creation.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <OpenGL/gl3.h>
#include "MicroGlut.h"
#include <sys/times.h>
// Linking hint for Lightweight IDE:
// uses framework Cocoa

// Compile shaders. Old junk code, I have better!
GLuint setupShader(const GLchar **vertSrc, const GLchar **fragSrc)
{
	GLuint	programObject;	// the program used to update
	GLuint	fragmentShader, vertexShader;
	
	programObject = glCreateProgram();

	if (fragSrc != NULL)
	{	
		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragmentShader, 1, fragSrc, NULL);
		glCompileShader(fragmentShader);
		glAttachShader(programObject, fragmentShader);
	}

	if (vertSrc != NULL)
	{	
		vertexShader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertexShader, 1, vertSrc, NULL);
		glCompileShader(vertexShader);
		glAttachShader(programObject, vertexShader);
	}

	glLinkProgram(programObject);
	GLint progLinkSuccess;
	glGetProgramiv(programObject, GL_LINK_STATUS,
		&progLinkSuccess);
	if (!progLinkSuccess)
	{
		fprintf(stderr, "Shader could not be linked\n");

		exit(1);
	}
	return programObject;
}

// Add offset (texUnit2) to string (texUnit)
// Negative values end up as > 0.5, adjust them!
static const char *fragSource =
{
"#version 150\n"
"uniform sampler2D texUnit;"
"uniform sampler2D texUnit2;"
"out vec4 outColor;"
"in vec2 texCoord;"
"void main(void)"
"{"
"   vec4 texVal  = texture(texUnit, texCoord);"
"   vec4 texVal2  = texture(texUnit2, texCoord);"
"   if (texVal2.r > 0.5) texVal2.r -= 1.0;"
"   if (texVal2.g > 0.5) texVal2.g -= 1.0;"
"   if (texVal2.b > 0.5) texVal2.b -= 1.0;"
"   if (texVal2.a > 0.5) texVal2.a -= 1.0;"
"   outColor = texVal;\n"
"   outColor = texVal2;\n"
"   outColor = texVal + texVal2;\n"
//"   outColor = vec4(0.2); // texVal + texVal2;\n"
//"   outColor = vec4(texCoord.s, texCoord.t, 1.0-texCoord.s, 1.0);\n"
"}"
};

// Vertex shader, pass position and texcoord
char *vs =
{
"#version 150\n"
"in  vec3 inPosition;"
"in vec2 inTexCoord;"
"out vec2 texCoord;"
"void main()"
"{"
"	texCoord = inTexCoord;"
"	gl_Position = vec4(inPosition, 1.0);"
"}"
};

GLfloat vertices[] = {	-1.0f,-1.0f,0.0f,
						-1.0f,1.0f,0.0f,
						1.0f,-1.0f,0.0f,
						
						1.0f,-1.0f,0.0f,
						-1.0f,1.0f,0.0f,
						1.0f,1.0f,0.0f };

GLfloat texcoord[] = {	0.0f, 1.0f,
						0.0f, 0.0f,
						1.0f, 1.0f,
						
						1.0f, 1.0f,
						0.0f, 0.0f,
						1.0f, 0.0f};

unsigned int vertexArrayObjID;

// declare texture size, the actual data will be a vector 
// of size texSize*1*4 = N

#define N 16
// test data
char a[N] = "Hello \0\0\0\0\0\0\0\0\0\0";
char b[N] = {15, 10, 6, 0, -12, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char c[N];
#define texSize 4

void display()
{
	glBindVertexArray(vertexArrayObjID);	// Select VAO
	glDrawArrays(GL_TRIANGLES, 0, 6);	// draw object
	glutSwapBuffers();

	printf("%s",a);
    // and read back
    glReadPixels(0, 0, texSize, 1, GL_RGBA,GL_UNSIGNED_BYTE,c);
    // print out results
    printf("%s\n",c);
	exit(0);
}

int main(int argc, char **argv)
{
    // set up glut to get valid GL context and 
    // get extension entry points
    glutInit (&argc, argv);
	glutInitContextVersion(3, 2);
	glutInitWindowSize (4, 1);
    glutCreateWindow("TEST1");
    
    // create string texture
    GLuint tex;
	glActiveTexture(GL_TEXTURE0);
    glGenTextures (1, &tex);
    glBindTexture(GL_TEXTURE_2D,tex);
    // define texture (standard format)
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,
                 texSize,1,0,GL_RGBA,GL_UNSIGNED_BYTE, a);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // create offset texture
    GLuint offtex;
	glActiveTexture(GL_TEXTURE1);
    glGenTextures (1, &offtex);
    glBindTexture(GL_TEXTURE_2D,offtex);
    // define texture (standard format)
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,
                 texSize,1,0,GL_RGBA,GL_UNSIGNED_BYTE, b);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// Compile shader
	GLuint shader;
	shader = setupShader(&vs, &fragSource);
	glUseProgram(shader);
// Inform shader of texture units
	glUniform1i(glGetUniformLocation(shader, "texUnit1"), 0); // Texture unit 0
	glUniform1i(glGetUniformLocation(shader, "texUnit2"), 1); // Texture unit 1

unsigned int vertexBufferObjID;
unsigned int texCoordBufferObjID;

// Allocate and activate Vertex Array Object
	glGenVertexArrays(1, &vertexArrayObjID);
	glBindVertexArray(vertexArrayObjID);
// Allocate Vertex Buffer Objects
	glGenBuffers(1, &vertexBufferObjID);
	glGenBuffers(1, &texCoordBufferObjID);
	
// VBO for vertex data
	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID);
	glBufferData(GL_ARRAY_BUFFER, 18*sizeof(GLfloat), vertices, GL_STATIC_DRAW);
	glVertexAttribPointer(glGetAttribLocation(shader, "inPosition"), 3, GL_FLOAT, GL_FALSE, 0, 0); 
	glEnableVertexAttribArray(glGetAttribLocation(shader, "inPosition"));

// VBO for texCoord data
	glBindBuffer(GL_ARRAY_BUFFER, texCoordBufferObjID);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat), texcoord, GL_STATIC_DRAW);
	glVertexAttribPointer(glGetAttribLocation(shader, "inTexCoord"), 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(glGetAttribLocation(shader, "inTexCoord"));

// Ask for a redraw
	glutDisplayFunc(display); 
	glutMainLoop();
	exit(0);
}
