
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "./glm/ext.hpp" // perspective, translate, rotate
#include <iostream>
#include "utils.h"
using namespace std;
float cameraX, cameraY, cameraZ;
float cubeLocX, cubeLocY, cubeLocZ;
#define numVAOs 1
#define numVBOs 1
// allocate variables used in display() function, so that they won’t need to be allocated during rendering
GLuint mvLoc, projLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat;
void setupVertices(void);
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
//float x = 0.0f; // location of triangle on x axis
//float inc = 0.01f; // offset for moving the triangle

void init(GLFWwindow* window) {
	renderingProgram = createShaderProgram(); //load, compile, create, attach and link shader programmes done here
	cameraX = 0.0f; cameraY = 0.0f; cameraZ = 8.0f; //camer position
	cubeLocX = 0.0f; cubeLocY = -2.0f; cubeLocZ = 0.0f; // shift cube down Y to reveal perspective
	// build perspective matrix
	glfwGetFramebufferSize(window, &width, &height); //need these to fit clip space to window
	aspect = (float)width / (float)height;
	pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees
	setupVertices(); //initial vertices x,y,z values
}
void display(GLFWwindow* window, double currentTime)
//runs 60 times a second by default in glfw - see main loop
{
	glClear(GL_DEPTH_BUFFER_BIT);  
	glClearColor(0.8, 0.8, 0.9, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
	glUseProgram(renderingProgram); //specify linked program to use

	//get a reference to the shader variable Vcol
	GLuint VcolLoc=glGetUniformLocation(renderingProgram,"Vcol");
	glUniform3f(VcolLoc, 0.0, 1.0, 0.0);  //set shader variable Vcol to rgb = green (see also vertShader.glsl)
	// and get references to the uniform variables from vertex shader for the MV and projection matrices
	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");

	// build view matrix, model matrix, and model-view matrix. Two ways shown here - lookat or form the view matrix manually
	//lookat used here
	//vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
	vMat=glm::lookAt(glm::vec3(cameraX, cameraY, cameraZ+1), glm::vec3(cubeLocX, cubeLocY, cubeLocZ),glm::vec3(0.0f, 1.0f, 0.0f));
	//create model matrix mMat=T*R
	//create translate matrix and mult by /identity matrix, I. This is first transformation of model matrix
	//mMat = I*T
	mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cubeLocX, cubeLocY, cubeLocZ)); 
	//mMat=I*T*R
	mMat = glm::rotate(mMat, glm::radians(45.0f), glm::vec3(0.0f, 1.0f, 0.0f));  //create rotation matrix and 
	//create model view matrix
	mvMat = vMat * mMat;
	// copy perspective and MV matrices to corresponding uniform variables
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
	// adjust OpenGL settings and draw model
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); //three vertices of triangle 1 primitive
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	
	/*glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);*/

	//glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	//  glPolygonMode(GL_BACK,GL_FILL); //uses also GL_FRONT_AND_BACK; GL_FILL, GL_POINT, GL_LINE  
	//  glPolygonMode(GL_FRONT,GL_FILL);// to spec. whether poly's. front and back are drawn filled
										// as lines or points
	//glCullFace(GL_BACK);//discards front or back facing polygons
	//glCullFace(GL_FRONT);//discards front or back facing polygons
	glDisable(GL_CULL_FACE);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	glDrawArrays(GL_TRIANGLES, 0, 48);  //draw first house
	
	//Create new model matrix and then mv matrix (no change to camera position/orientation so view matrix not touched)
	mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cubeLocX+2, cubeLocY, cubeLocZ));
	mMat = glm::rotate(mMat, glm::radians(45.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	mvMat = vMat * mMat;
	//copy new mv matrix to shader	
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
    glUniform3f(VcolLoc, 1.0, 0.0, 0.0); //set the shader variable Vcol to red before..
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	glDrawArrays(GL_TRIANGLES, 0, 48);  //drawing a second house
	/*glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glDrawArrays(GL_TRIANGLES, 0, 3);*/
}
int main(void) {
	if (!glfwInit()) { exit(EXIT_FAILURE); }
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	GLFWwindow* window = glfwCreateWindow(600, 600, "Triangle glfw window", NULL, NULL);
	glfwMakeContextCurrent(window);
	if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
	glfwSwapInterval(1);
	init(window);
	while (!glfwWindowShouldClose(window)) {
		display(window, glfwGetTime());
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);
}


void setupVertices(void) {
	// 9 vertices, for first triangle
	float tri1VertexPos[144] = {
		//A House
			 0.0, 0.0, 0.0,//front 
			 0.0, -2.0, 0.0,
			 1.0, 0.0, 0.0,
			0.0, -2.0, 0.0,
			1.0, -2.0, 0.0,
			1.0, 0.0, 0.0,

			1.0, 0.0, 0.0,//right 
			1.0, -2.0, 0.0,
			1.0, -2.0, -1.0,
			1.0, 0.0, 0.0,
			1.0, -2.0, -1.0,
			1.0, 0.0, -1.0,

			0.0, 0.0, 0.0,//left 
			0.0, 0.0, -1.0,
			0.0, -2.0, -1.0,
			0.0, 0.0, 0.0,
			0.0, -2.0, -1.0,
			0.0, -2.0, 0.0,

			0.0, -2.0, 0.0, //bottom
			0.0, -2.0, -1.0,
			1.0, -2.0, -1.0,
			0.0, -2.0, 0.0,
			1.0, -2.0, -1.0,
			1.0, -2.0, 0.0,

			0.0, 0.0, -1.0,//back 
			1.0, -2.0, -1.0,
			1.0, 0.0, -1.0,
			0.0, 0.0, -1.0,
			0.0, -2.0, -1.0,
			1.0, -2.0, -1.0,
		0.0, 0.0, 0.0,//front gable 
			1.0, 0.0, 0.0,
			0.5, 1.25, 0.0,

			0.0, 0.0, -1.0,//back gable 
			0.5, 1.25, -1.0,
			1.0, 0.0, -1.0,


			0.0, 0.0, 0.0,//left roof 
			0.5, 1.25, 0.0,
			0.5, 1.25, -1.0,
			0.0, 0.0, 0.0,
			0.5, 1.25, -1.0,
			0.0, 0.0, -1.0,

			0.5, 1.25, 0.0,//right roof 
			1.0, 0.0, 0.0,
			1.0, 0.0, -1.0,
			0.5, 1.25, 0.0,
			1.0, 0.0, -1.0,
			0.5, 1.25, -1.0
	};



	//initialse VAO and VBOs
	glGenVertexArrays(numVAOs, vao); //we will use one VAO
	glBindVertexArray(vao[0]);
	glGenBuffers(numVBOs, vbo);//we have one VBO
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//first VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(tri1VertexPos), tri1VertexPos, GL_STATIC_DRAW);
	//glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//first VBO
	//glBufferData(GL_ARRAY_BUFFER, sizeof(tri1VertexColor), tri1VertexColor, GL_STATIC_DRAW);
	//glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);//3nd VBO
	//glBufferData(GL_ARRAY_BUFFER, sizeof(tri2VertexPos), tri2VertexPos, GL_STATIC_DRAW);
}
