﻿#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "./glm/ext.hpp" // perspective, translate, rotate
#include <iostream>
#include "utils.h"
#include "stb_imageloader.h"
using namespace std;
float cameraX, cameraY, cameraZ;
float cubeLocX, cubeLocY, cubeLocZ;
int direction = 0;
GLuint tex[2];
#define numVAOs 1
#define numVBOs 2
// 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 moveCamera(float Px, float Pz, float Vtorx, float Vtorz)
{
	cout << "Px" << Px << "  Pz=" << Pz << "  Vtorx=" << Vtorx << "  Vtorz=" << Vtorz << endl;
	vMat = glm::lookAt(glm::vec3(Px, -2.0, Pz), glm::vec3(Px + Vtorx, -2.0, Pz + Vtorz), glm::vec3(0.0, 1.0, 0.0));
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
	static int last = 0;
	static float Px = 0, Py = -2, Pz = 18, Vtorx = 0, Vtorz = -1.0, angle = 0;
	switch (key) {
	case GLFW_KEY_A:
		//look left
		angle = angle - 5;
		Vtorx = sin(angle * 0.01745);  //
		Vtorz = -cos(angle * 0.01745); //assume camera default is pointing down negative z axis.
		break;
	case GLFW_KEY_D:
		//look right
		angle = angle + 5;
		Vtorx = sin(angle * 0.01745);  //
		Vtorz = -cos(angle * 0.01745); //assume camera default is pointing down negative z axis.
		break;
	case GLFW_KEY_W:
		//move forward along line of sight
		Px = Px + 0.1 * Vtorx;
		Pz = Pz + 0.1 * Vtorz;
		break;
	case GLFW_KEY_S:
		//move backwards along line of sight
		Px = Px - 0.1 * Vtorx;
		Pz = Pz - 0.1 * Vtorz;
		//vz= vz + 0.1;
		break;
	case GLFW_KEY_Z:
		//strafe left
		cout << "A pressed" << last << endl;
		last++;
		direction++;
		break;
	case GLFW_KEY_X:
		//strafe right
		cout << "D pressed" << last << endl;
		direction--;
		break;
	case 27:
		exit(0);
		break;
	default:
		break;
	}
	moveCamera(Px, Pz, Vtorx, Vtorz); //best placed where you’d normally use glm::lookat
}




void init(GLFWwindow* window) {

	int x, y, n;
	int force_channels = 4; //RGB and A
	char filename[] = "checker.png";
	char filename2[] = "checker2.png";
	unsigned char* image_data = stbi_load(filename , &x, &y, &n, force_channels);  //x = x dimemsions, y= ydims, 
	if (!image_data)
	{
		cout << "error: could not load " << filename << endl;

	}
	// copy image into ogl buffer - stick to images power of 2 high and wide
	glGenTextures(2, tex);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, tex[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
image_data = stbi_load(filename2, &x, &y, &n, force_channels);  //x = x dimemsions, y= ydims, 
	if (!image_data)
	{
		cout << "error: could not load " << filename2 << endl;

	}
	
	glBindTexture(GL_TEXTURE_2D, tex[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	
	renderingProgram = createShaderProgram(); //load, compile, create, attach and link shader programmes done here
	cameraX = 0.0f; cameraY = 0.0f; cameraZ = 18.0f; //camera position
	cubeLocX = 0.0f; cubeLocY = -2.0f; cubeLocZ = 0.0f; // shift cube down Y to reveal perspective
	vMat = glm::lookAt(glm::vec3(cameraX, cameraY, cameraZ), glm::vec3(cubeLocX, cubeLocY, cubeLocZ), glm::vec3(0.0f, 1.01f, 0.0f));
	// 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.05f, 100.0f); // 1.0472 radians = 60 degrees
	setupVertices(); //initial vertices x,y,z values
	glEnable(GL_DEPTH_TEST);
}
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");
	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");

	// 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);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); //house mesh bound to VBO
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);
	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
	
	int k = 0;
	for (int i = 0; i < numStreets; i++)
	{
		for (int j = 0; j < numHouses; j++)
		{
			xOffset = j * 2;
			yOffset = i * 2;
			if (j % 2 == 0) //even houses only
			{
				glBindTexture(GL_TEXTURE_2D, tex[0]); //change textures
			}
			else
			{
				glBindTexture(GL_TEXTURE_2D, tex[1]);
			}
			//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(0.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, "glfw window", NULL, NULL);
	glfwSetKeyCallback(window, key_callback);
	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) {

	float house1VertexPos[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
	};
		float house1Tex[96] = {
		//A House
	/*			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
	0.0, 0.0, 0.0,*/
		//front 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//right 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//left 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//bottom
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//back 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//front gable 
			0.0, 1.0,
			0.0, 0.0,
			1.0, 0.0,
			//back gable 
			0.0, 1.0,
			0.0, 0.0,
			1.0, 0.0,
			//left roof 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0,
			//right roof 
			1.0, 0.0,
			1.0, 1.0,
			0.0, 1.0,
			1.0, 0.0,
			0.0, 1.0,
			0.0, 0.0
		};

		float house2VertexPos[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
		};
		float house2Colour[144] = {
			//A House
		/*			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
		0.0, 0.0, 0.0,*/
		//front 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//right 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//left 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//bottom
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//back 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//front gable 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//back gable 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//left roof 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			//right roof 
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.0,
			1.0, 1.0, 0.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(house1VertexPos), house1VertexPos, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//first VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(house1Tex), house1Tex, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);//3nd VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(house2VertexPos), house2VertexPos, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);//4th VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(house2Colour), house2Colour, GL_STATIC_DRAW);


}