﻿#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[4];

#define numVAOs 3
#define numVBOs 6
// 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) //test 
{
	cout << "Px" << Px << "  Pz=" << Pz << "  Vtorx=" << Vtorx << "  Vtorz=" << Vtorz << endl;
	vMat = glm::lookAt(glm::vec3(Px, 2.0, Pz)/*here is the anigle*/, glm::vec3(Px + Vtorx, 0.0, Pz + Vtorz), glm::vec3(0.0, 1.0, 0.0)); //look up and down feature
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
	
	static int last = 0;
	static float Px = -1.0, Py = 6, Pz = -1.5, Vtorx = 0, Vtorz = 1.0, angle = 1; // starting location 
	switch (key) {
	//case GLFW_KEY_A:
	//	//look left
	//	angle = angle - 2;
	//	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";
	char filename3[] = "table.png";
	char filename4[] = "blocker.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(3, 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);

	image_data = stbi_load(filename3, &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[2]);
	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);

	image_data = stbi_load(filename4, &x, &y, &n, force_channels);  //x = x dimemsions, y= ydims, 
	if (!image_data)
	{
		cout << "error: could not load " << filename4 << endl;

	}

	glBindTexture(GL_TEXTURE_2D, tex[3]);
	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 = -10.0f; cameraZ = 0.0f; //camera position
	cubeLocX = 0.0f; cubeLocY = -1.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
	glBindVertexArray(vao[0]);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	int xOffset = 0, yOffset = 0;
	int numlines = 3;
	int numCards = 2;
	
	//cards 
	int k = 0;
	for (int i = 0; i < numCards; i++)
	{
		for (int j = 0; j < numlines; j++)
		{
			xOffset = j * 3;
			yOffset = i * 3;
			if (j % 2 == 0) 
			{
				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));
			
			k = k + 3; //next colour
			glDrawArrays(GL_TRIANGLES, 0, 48);  

		}
	}
	
	
	//Adding another object to scene.
	//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 , cubeLocY, cubeLocZ + yOffset + 5));
	mMat = glm::rotate(mMat, glm::radians(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	mvMat = vMat * mMat;
	glBindVertexArray(vao[1]);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); 
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[3]); 
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);
	

	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
	glBindTexture(GL_TEXTURE_2D, tex[2]);
	glDrawArrays(GL_TRIANGLES, 0, 18);  //draw a CARD
	
	/// ////////////////////////////////////////////////
	//draw a blocker
	mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cubeLocX, cubeLocY, cubeLocZ + yOffset + 5));//here
	mMat = glm::rotate(mMat, glm::radians(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	mvMat = vMat * mMat;
	glBindVertexArray(vao[2]);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[4]); 
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[5]); 
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);
	
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
	glBindTexture(GL_TEXTURE_2D, tex[3]);
	glDrawArrays(GL_TRIANGLES, 0, 18);  



}

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 cardsPos[18] = { 

		-3.0, -1.0, -0.0, 
			-2.0, -1.0, 0.0,
			-3.0, -1.0, -2.0,

			-3.0, -1.0, -2.0,
			-2.0, -1.0, -2.0,
			-2.0, -1.0, -0.0,
		
	};
	float card1Tex[12] = { 
	
		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 table[18] = { // table 


		

		-7.0, -2.0, -2.0, //table
			-7.0, -2.0, -12.0,
			8.0, -2.0, -12.0,
			-7.0, -2.0, -2.0,
			8.0, -2.0, -12.0,
			8.0, -2.0, -2.0,

			
	};
	float card2Tex[36] = {
	
		 
		0.0, 1.0,
		0.0, 0.0,
		1.0, 0.0,
		
		0.0, 1.0,
		0.0, 0.0,
		1.0, 0.0,
		 
		1.0, 0.0,
		1.0, 1.0,
		0.0, 1.0,
		1.0, 0.0,
		0.0, 1.0,
		0.0, 0.0,
		
		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 block1Pos[18] = {
			/**/10.0, 0.0, 5.0,
			10.0, 0.0, -5.0,
			-5.0, 0.0, -5.0,

			10.0, 0.0, 5.0,
			-5.0, 0.0, 5.0,
			-5.0, 0.0, -5.0,
			/*
			 15.0, 0.0, 10.0, 
			15.0, 0.0, -10.0, 
			-6.0, 0.0, -10.0, 
			15.0, 0.0, 10.0, 
			-6.0, 0.0, 10.0, 
			-6.0, 0.0, -10.0,
			*/
		};

		float blockTex1[12] = {
		1.0, 0.0,
		1.0, 1.0,
		0.0, 1.0,
		1.0, 0.0,
		0.0, 1.0,
		0.0, 0.0,

		};
	//initialse VAO and VBOs
	glGenVertexArrays(numVAOs, vao); // VAO 1
	glBindVertexArray(vao[0]);
	glGenBuffers(numVBOs, vbo);//
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//first VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(cardsPos), cardsPos, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//2nd VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(card1Tex), card1Tex, GL_STATIC_DRAW);

	glBindVertexArray(vao[1]); //VAO 2
	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);//3nd VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(table), table, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);//4th VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(card2Tex), card2Tex, GL_STATIC_DRAW);

	glBindVertexArray(vao[2]); //VAO 3
	glBindBuffer(GL_ARRAY_BUFFER, vbo[4]);//5th VBO
	glBufferData(GL_ARRAY_BUFFER, sizeof(block1Pos), block1Pos, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[5]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(blockTex1), blockTex1, GL_STATIC_DRAW);

}