
#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];


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; //camera 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");

	vMat=glm::lookAt(glm::vec3(cameraX, cameraY+10, 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
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); //house mesh bound to VBO
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	int xOffset = 0, yOffset = 0;
	int numHouses = 3;
	int numStreets = 2;
	//draw 6 houses in two streets of three
	float houseCols[18] = {
	1.0,0.0,0.0,
	1.0,1.0,0.0,
	174.0 / 255.0,85.0 / 255.0,186.0 / 255.0,
	0.0,1.0,0.0,
	0.0,0.0,1.0,
	233.0 / 255.0,66.0 / 255.0,17.0 / 255.0
	};
	int k = 0;
	for (int i = 0; i < numStreets ; i++)
	{
		for (int j = 0; j < numHouses; j++)
		{
			xOffset = j * 2;
			yOffset = i * 2;

			//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 + xOffset, cubeLocY , cubeLocZ+ yOffset));
			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, houseCols[k], houseCols[k+1], houseCols[k+ 2]); //set the shader variable Vcol to red before..
			k=k+3; //next colour
			glDrawArrays(GL_TRIANGLES, 0, 48);  //draw a house

		}
	}
}
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);
}
