// Lab1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

//Utilized the following resources to complete this lab:
//http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
//http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
//Lab 0 -- SimpleGLUT.cpp
//



#include "pch.h"
#include <iostream>
using namespace std;
//#include "stdafx.h"

// standard
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <vector>
#define GLM_ENABLE_EXPERIMENTAL
// glut
#include <GL/glut.h>



// Include GLFW
//#include <GLFW/glfw3.h>
//GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/norm.hpp>
#include <glm/gtx/spline.hpp>
#include <glm/gtx/string_cast.hpp>

using namespace glm;

//================================
// global variables
//================================
// screen size
int g_screenWidth = 0;
int g_screenHeight = 0;

// frame index
int g_frameIndex = 0;

int rcount = 0;
//outputs for interpolations (Catmull Rom)
vec3 anglePos;
vec3 angleOri;
vec3 quatPos;
quat quatOri;
vec3 anglePosList[100];
vec3 angleOriList[100];
vec3 quatPosList[100];
quat quatOriList[100];
vec3 aquatOriList[100];
//outputs for interpolations (B)
vec3 pFinal;
vec3 oFinal;
vec3 qPFinal;
quat qOFinal;
vec3 bAnglePosList[100];
vec3 bAngleOriList[100];
vec3 bQuatPosList[100];
quat bQuatOriList[100];
vec3 abquatOriList[100];

//outputs for Rotations
quat norm;
mat4 eRot;
mat4 qRot;

//Initialize my Fixed Angle inputs (3 for coordinates, 3 for angles)
vec3 positions[3];
vec3 orientations[3];
vec3 ePosition1(0.0f, 0.0f, -5.0f);
vec3 eOrientation1(0, 0, 0);
vec3 ePosition2(0.1f, 0.1f, -5.0f);
vec3 eOrientation2(1.5708, 0, 0);
vec3 ePosition3(0.2f, 0.2f, -5.0f);
vec3 eOrientation3(0, 0, 0);
vec3 ePosition4(0.3f, 0.3f, -5.0f);
vec3 eOrientation4(0, 0, 0);

//Initialize my Quaternion inputs(3 for translation, 4 for rotation)
vec3 qPosition1(0.0f, 0.0f, -5.0f);
quat qOrientation1(.45, 0, 0, 0);
vec3 qPosition2(0.1f, 0.0f, -5.1f);
quat qOrientation2(0, 0, 1, 0);
vec3 qPosition3(0.2f, 0.0f, -4.8f);
quat qOrientation3(0, 0, 2, 0);
vec3 qPosition4(-0.1f, 0.0f, -5.0f);
quat qOrientation4(0, 0, 3, 0);


void fixedCatmullRom(void) {
	int j = 0;
	for (double i = .1; i < 10; i = i + .1) {
		
		anglePos = glm::catmullRom(ePosition1, ePosition2, ePosition3, ePosition4, i);
		anglePosList[j] = anglePos;
		j++;
		
	}

	j = 0;
	for (double i = .1; i < 10; i = i + .1) {
		
		angleOri = glm::catmullRom(eOrientation1, eOrientation2, eOrientation3, eOrientation4, i);
		angleOriList[j] = angleOri;
		j++;
	}
}

void quatCatmullRom(void) {
	
	glm::vec3 nOrientation1 = glm::eulerAngles(qOrientation1);
	glm::vec3 nOrientation2 = glm::eulerAngles(qOrientation2);
	glm::vec3 nOrientation3 = glm::eulerAngles(qOrientation3);
	glm::vec3 nOrientation4 = glm::eulerAngles(qOrientation4);
	
	int j = 0;
	for (double i = .1; i < 10; i = i + .1) {
		quatPos = glm::catmullRom(qPosition1, qPosition2, ePosition3, ePosition4, i);
		quatPosList[j] = quatPos;
		j = j+1;
		
	}

	j = 0;
	for (double i = .1; i < 10; i = i + .1) {
		quatOri = glm::catmullRom(qOrientation1, qOrientation2, qOrientation3, qOrientation4, i);
		quatOriList[j] = quatOri;
		aquatOriList[j] = glm::eulerAngles(quatOri);
		j++;
	}
}

void fixedBCurve(void) {
	int j = 0;
	for (double t = .1; t < 10; t = t + .1){
		//For position points
		pFinal.x = pow(1 - t, 3) * ePosition1.x + pow(1 - t, 2) * 3 * t * ePosition2.x + (1 - t) * 3 * t * t * ePosition3.x + t * t * t * ePosition4.x;
		pFinal.y = pow(1 - t, 3) * ePosition1.y + pow(1 - t, 2) * 3 * t * ePosition2.y + (1 - t) * 3 * t * t * ePosition3.y + t * t * t * ePosition4.y;
		pFinal.z = pow(1 - t, 3) * ePosition1.z + pow(1 - t, 2) * 3 * t * ePosition2.z + (1 - t) * 3 * t * t * ePosition3.z + t * t * t * ePosition4.z;
		bAnglePosList[j] = pFinal;
		j++;
	}

	j = 0;
	//For orientation points
	for (double t = .1; t < 10; t = t + .1){
		oFinal.x = pow(1 - t, 3) * eOrientation1.x + pow(1 - t, 2) * 3 * t * eOrientation2.x + (1 - t) * 3 * t * t * eOrientation3.x + t * t * t * eOrientation4.x;
		oFinal.y = pow(1 - t, 3) * eOrientation1.y + pow(1 - t, 2) * 3 * t * eOrientation2.y + (1 - t) * 3 * t * t * eOrientation3.y + t * t * t * eOrientation4.y;
		oFinal.z = pow(1 - t, 3) * eOrientation1.z + pow(1 - t, 2) * 3 * t * eOrientation2.z + (1 - t) * 3 * t * t * eOrientation3.z + t * t * t * eOrientation4.z;
		
		bAngleOriList[j] = oFinal;
	}
}

void quatBCurve(void) {
	int j = 0;
	for (double t = .1; t < 10; t = t + .1) {
		//For position points
		qPFinal.x = pow(1 - t, 3) * qPosition1.x + pow(1 - t, 2) * 3 * t * qPosition2.x + (1 - t) * 3 * t * t * qPosition3.x + t * t * t * qPosition4.x;
		qPFinal.y = pow(1 - t, 3) * qPosition1.y + pow(1 - t, 2) * 3 * t * qPosition2.y + (1 - t) * 3 * t * t * qPosition3.y + t * t * t * qPosition4.y;
		qPFinal.z = pow(1 - t, 3) * qPosition1.z + pow(1 - t, 2) * 3 * t * qPosition2.z + (1 - t) * 3 * t * t * qPosition3.z + t * t * t * qPosition4.z;
		bQuatPosList[j] = qPFinal;
		j++;
	}

	j = 0;
	//For orientation points
	for (double t = .1; t < 10; t = t + .1) {
		qOFinal.w = pow(1 - t, 3) * qOrientation1.w + pow(1 - t, 2) * 3 * t * qOrientation2.w + (1 - t) * 3 * t * t * qOrientation3.w + t * t * t * qOrientation4.w;
		qOFinal.x = pow(1 - t, 3) * qOrientation1.x + pow(1 - t, 2) * 3 * t * qOrientation2.x + (1 - t) * 3 * t * t * qOrientation3.x + t * t * t * qOrientation4.x;
		qOFinal.y = pow(1 - t, 3) * qOrientation1.y + pow(1 - t, 2) * 3 * t * qOrientation2.y + (1 - t) * 3 * t * t * qOrientation3.y + t * t * t * qOrientation4.y;
		qOFinal.z = pow(1 - t, 3) * qOrientation1.z + pow(1 - t, 2) * 3 * t * qOrientation2.z + (1 - t) * 3 * t * t * qOrientation3.z + t * t * t * qOrientation4.z;
		bQuatOriList[j] = qOFinal;
		abquatOriList[j] = glm::eulerAngles(qOFinal);
		j++;
	}
}

void fixedToRotation(vec3 Point) {
	
	glRotated(Point.x, 1.0, 0.0, 0.0);
	glRotated(Point.y, 0.0, 1.0, 0.0);
	glRotated(Point.z, 0.0, 0.0, 1.0);
	

}

void readyForRender(vec3 Pos, vec3 Ori) {
	glTranslatef(Pos.x, Pos.y, Pos.z);
	fixedToRotation(Ori);
}








//================================
// render
//================================
void render(void) {
	// clear buffer
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClearDepth(1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// render state
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);

	// enable lighting
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	// light source attributes
	GLfloat LightAmbient[] = { 0.4f, 0.4f, 0.4f, 1.0f };
	GLfloat LightDiffuse[] = { 0.3f, 0.3f, 0.3f, 1.0f };
	GLfloat LightSpecular[] = { 0.4f, 0.4f, 0.4f, 1.0f };
	GLfloat LightPosition[] = { 5.0f, 5.0f, 5.0f, 1.0f };

	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);

	// surface material attributes
	GLfloat material_Ka[] = { 0.11f, 0.06f, 0.11f, 1.0f };
	GLfloat material_Kd[] = { 0.43f, 0.47f, 0.54f, 1.0f };
	GLfloat material_Ks[] = { 0.33f, 0.33f, 0.52f, 1.0f };
	GLfloat material_Ke[] = { 0.1f , 0.0f , 0.1f , 1.0f };
	GLfloat material_Se = 10;

	glMaterialfv(GL_FRONT, GL_AMBIENT, material_Ka);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, material_Kd);
	glMaterialfv(GL_FRONT, GL_SPECULAR, material_Ks);
	glMaterialfv(GL_FRONT, GL_EMISSION, material_Ke);
	glMaterialf(GL_FRONT, GL_SHININESS, material_Se);

	
	// modelview matrix
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	readyForRender(bQuatPosList[rcount], abquatOriList[rcount]);
		// render objects
	glutSolidTeapot(.5);
	
	if (rcount < 100) {
		rcount = rcount + 1;
	}
	else {
		rcount = 0;
	}

	// disable lighting
	glDisable(GL_LIGHT0);
	glDisable(GL_LIGHTING);

	// swap back and front buffers
	glutSwapBuffers();
}

//================================
// keyboard input
//================================
void keyboard(unsigned char key, int x, int y) {
}

//================================
// reshape : update viewport and projection matrix when the window is resized
//================================
void reshape(int w, int h) {
	// screen size
	g_screenWidth = w;
	g_screenHeight = h;

	// viewport
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);

	// projection matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 1.0, 2000.0);
}
//================================
// timer : triggered every 16ms ( about 60 frames per second )
//================================
void timer(int value) {
	// increase frame index
	g_frameIndex++;

	

	// render
	glutPostRedisplay();

	// reset timer
	// 16 ms per frame ( about 60 frames per second )
	glutTimerFunc(16, timer, 0);
}

int main(int argc, char** argv)
{

	fixedCatmullRom();
	quatCatmullRom();
	fixedBCurve();
	quatBCurve();

	// create opengL window
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(600, 600);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);

	// set callback functions
	glutDisplayFunc(render);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutTimerFunc(16, timer, 0);

	// main loop
	glutMainLoop();
	
	
	
	//DEBUGGER
	//for (int i = 0; i < 10; i++) {
	//std:cout << glm::to_string(anglePosList[i]) << std::endl;
	//std:cout << glm::to_string(angleOriList[i]) << std::endl;
	//std:cout << glm::to_string(quatPosList[i]) << std::endl;
	//std:cout << glm::to_string(quatOriList[i]) << std::endl;
		//std::cout << glm::to_string(bAnglePosList[i]) << std::endl;
		//std::cout << glm::to_string(bAngleOriList[i]) << std::endl;
		//std::cout << glm::to_string(bQuatPosList[i]) << std::endl;
		//std::cout << glm::to_string(bQuatOriList[i]) << std::endl;
		//std::cout << (readyForRender(anglePosList[i], angleOriList[i])) << std::endl;
		
	//}

	return 0;


	

}