
#include "simple_camera_gl3.h"
#include <iostream>

using namespace std;

///constructeur par défaut de la caméra
SimpleCameraGl3::SimpleCameraGl3(){
	this->initialisation(VECT3F_NULL, 0.0, M_PI/2.0, M_PI/2.0);
}

///constructeur de SimpleCameraGl3
/**	@param position : position de la SimpleCameraGl3
 * 	@param theta : angle de rotation selon l'axe vertical de la SimpleCameraGl3 (rotation de gauche à droite)
 *	@param  phi: angle de rotation selon l'axe gauche de la SimpleCameraGl3 (rotation de haut en bas)
 * 	@param rouli : angle de rotation selon l'axe de visée de la SimpleCameraGl3 (Rouli)
*/
SimpleCameraGl3::SimpleCameraGl3(const Vecteur3f& position, float theta, float phi, float rouli){
	this->initialisation(position, theta, phi, rouli);
}

///constructeur de copie de la SimpleCameraGl3
/** @param camera : SimpleCameraGl3 à copier
*/
SimpleCameraGl3::SimpleCameraGl3(const SimpleCameraGl3 & camera){
	this->copyCamera(camera);
}

///destructeur de la Caméra
SimpleCameraGl3::~SimpleCameraGl3(){}

///fonction qui met en route la caméra et dit a OpenGl où il faut regarder
void SimpleCameraGl3::see(Matrice4& matriceModelview){
	updateCameraGl3();
// 	Vecteur3f object(p_position + p_visee);
// 	matriceModelview.lookAt(p_position.getx(), p_position.getz(), p_position.getz(), object.getx(), object.gety(), object.getz(), p_up.getx(), p_up.gety(), p_up.getz());
	
	matriceModelview.lookAt(p_position, p_visee, -p_planGauche, p_up);
	
// 	matriceModelview.rotateRad(-p_theta, p_up.getx(), p_up.gety(), p_up.getz());
// 	matriceModelview.rotateRad(-p_theta, 0.0, 0.0, 1.0);
// 	matriceModelview.rotateRad(-p_phi, 0.0, 1.0, 0.0);
// 	matriceModelview.rotateRad(-p_rouli, 1.0, 0.0, 0.0);
// 	matriceModelview.translated(-p_position.getx(), -p_position.gety(), -p_position.getz());
}

///initialise la caméra
/**	@param position : position de la caméra
	@param theta : angle de la caméra dans le plan xOy avec l'axe Ox
	@param phi : angle de la caméra dans le plan xOz avec l'axe Oz
	@param rouli : angle de la caméra dans le plan yOz avec l'axe Oz
*/
void SimpleCameraGl3::setAll(const Vecteur3f& position, float theta, float phi, float rouli){
	this->initialisation(position, theta, phi, rouli);
}

///intialise la position de la caméra
/** @param position : position de la caméra
*/
void SimpleCameraGl3::setPosition(const Vecteur3f& position){p_position = position;}

///intialise la position de la caméra
/**	@param x : position en x
 * 	@param y : position en y
 * 	@param z : position en z
*/
void SimpleCameraGl3::setPosition(float x, float y, float z){
	p_position.setXYZ(x, y, z);
}

///fonction qui initialise les angles de la caméra
/**	@param theta : angle de la caméra dans le plan xOy avec l'axe Ox
	@param phi : angle de la caméra dans le plan xOz avec l'axe Oz
	@param rouli : angle de la caméra dans le plan yOz avec l'axe Oz
*/
void SimpleCameraGl3::setAngles(float theta, float phi,float rouli){
	p_theta = theta;
	p_phi = phi;
	p_rouli = rouli;
}

///initialise l'angle theta de la caméra
/**	@param theta : angle de la caméra dans le plan xOy avec l'axe Ox
*/
void SimpleCameraGl3::setTheta(float theta){p_theta = theta;}

///initialise l'angle phi de la caméra
/** @param phi : angle de la caméra dans le plan xOz avec l'axe Oz
*/
void SimpleCameraGl3::setPhi(float phi){p_phi = phi;}

///initialise l'angle rouli de la caméra
/** @param rouli : angle de la caméra dans le plan yOz avec l'axe Oz
*/
void SimpleCameraGl3::setRouli(float rouli){p_rouli = rouli;}

///fonction qui permet d'initialiser l'id de la fenêtre parente de la caméra
/**	@param id : id de la fenêtre parente de la caméra
*/
void SimpleCameraGl3::setWindowID(SDL_WindowID id){p_windowId = id;}

///déplace la caméra de dm
/** @param dm : déplacement de la caméra
*/
void SimpleCameraGl3::move(const Vecteur3f& dm){p_position += dm;}

///fonction qui permet de déplacer la caméra selon son axe de visée
/** @param dl : distance à parcourir selon l'axe de visée
*/
void SimpleCameraGl3::moveVisee(float dl){
	p_position += p_visee*dl;
}

///fonction qui permet de déplacer la caméra selon la gauche de la caméra
/** @param dl : distance à parcourir selon la gauche de la caméra
*/
void SimpleCameraGl3::moveGauche(float dl){
	p_position += p_planGauche*dl;
}

///fonction qui permet de faire pencher la caméra d'avant en arrière
/** @param dPhi : modification de l'angle
*/
void SimpleCameraGl3::turnUp(float dPhi){p_phi -= dPhi;}

///fonction qui permet de faire pencher la caméra d'arrière en avant
/** @param dPhi : modification de l'angle
*/
void SimpleCameraGl3::turnDown(float dPhi){p_phi += dPhi;}

///fonction qui permet de faire tourner la caméra de gauche à droite
/** @param dTheta : modification de l'angle
*/
void SimpleCameraGl3::turnRight(float dTheta){
	if(p_up.getz() > 0) p_theta -= dTheta;
	else p_theta += dTheta;
}

///fonction qui permet de faire tourner la caméra de droite à gauche
/** @param dTheta : modification de l'angle
*/
void SimpleCameraGl3::turnLeft(float dTheta){
	if(p_up.getz() > 0) p_theta += dTheta;
	else p_theta -= dTheta;
}

///fonction qui change l'angle theta de la caméra
/** @param dTheta : changement de l'angle theta
*/
void SimpleCameraGl3::addTheta(float dTheta){p_theta += dTheta;}

///fonction qui change l'angle phi de la caméra
/** @param dPhi : changement de l'angle phi
*/
void SimpleCameraGl3::addPhi(float dPhi){p_phi += dPhi;}

///fonction qui change l'angle rouli de la caméra
/** @param dRouli : changement de l'angle rouli
*/
void SimpleCameraGl3::addRouli(float dRouli){p_rouli += dRouli;}


///change le rotation de la caméra de dTheta, dPhi et dRouli
/**	@param dTheta : variation de l'angle theta (angle de la caméra dans le plan xOy avec l'axe Ox)
	@param dPhi : variation de l'angle phi (angle de la caméra dans le plan xOz avec l'axe Oz)
	@param dRouli : variation de l'angle rouli (angle de la caméra dans le plan yOz avec l'axe Oz)
*/
void SimpleCameraGl3::rotation(float dTheta, float dPhi,float dRouli){
	p_theta += dTheta;
	p_phi += dPhi;
	p_rouli += dRouli;
}

///renvoie la position de la caméra
/** @return position de la caméra
*/
Vecteur3f SimpleCameraGl3::getPosition() const{return p_position;}

///renvoie la visée de la caméra (Vecteur3f unitaire)
/** @return visée de la caméra (Vecteur3f unitaire)
*/
Vecteur3f SimpleCameraGl3::getVisee(){
	return p_visee;
}

///renvoie le Vecteur3f vertical de la caméra (Vecteur3f unitaire)
/** @return Vecteur3f vertical de la caméra (Vecteur3f unitaire)
*/
Vecteur3f SimpleCameraGl3::getUp(){
	return p_up;
}

///renvoie le Vecteur3f gauche de la caméra (Vecteur3f unitaire)
/** @return Vecteur3f gauche de la caméra (Vecteur3f unitaire)
*/
Vecteur3f SimpleCameraGl3::getPlanGauche(){
	return p_planGauche;
}

///renvoie l'angle theta de la caméra
/** @return angle theta de la caméra
*/
float SimpleCameraGl3::getTheta() const{return p_theta;}

///renvoie l'angle phi de la caméra
/** @return angle phi de la caméra
*/
float SimpleCameraGl3::getPhi() const{return p_phi;}

///renvoie l'angle rouli de la caméra
/** @return angle rouli de la caméra
*/
float SimpleCameraGl3::getRouli() const{return p_rouli;}


///mode FPS de la caméra (caméra à la premère)
/**	@param mouseX : position de la souris en X
 * 	@param mouseY : position de la souris en Y
 * 	@param midWidth : moitié de la longueur de la fenêtre
 * 	@param midHeight : moitié de la hauteur de la fenêtre
 * 	@param coefSensibiliteX : sensibilité en x
 * 	@param coefSensibiliteY : sensibilité en y
 * 	@param phiMin : inclinaison minimum de la caméra
 * 	@param phiMax : inclinaison maximum de la caméra
*/
void SimpleCameraGl3::modeFPS(float mouseX, float mouseY, float midWidth, float midHeight, float coefSensibiliteX, float coefSensibiliteY, float phiMin, float phiMax){
	p_theta = ((float)(mouseX - midWidth))*coefSensibiliteX;
	p_phi = ((float)(mouseY - midHeight))*coefSensibiliteY;
// 	theta = 0.0;
	if(p_phi < phiMin){
		p_phi = phiMin;
		//en fait il faut utiliser :
// 		SDL_WarpMouseInWindow(p_windowId, midWidth + p_theta/coefSensibiliteX , midHeight + phiMin/coefSensibiliteY);
	}
	if(p_phi > phiMax){
		p_phi = phiMax;
// 		SDL_WarpMouseInWindow(p_windowId, midWidth + p_theta/coefSensibiliteX , midHeight + phiMax/coefSensibiliteY);
	}
	
	if(p_theta < -180){
		p_theta = 180 + (p_theta + 180);
// 		SDL_WarpMouseInWindow(p_windowId, midWidth + p_theta/coefSensibiliteX , midHeight + p_phi/coefSensibiliteY);
	}
	if(p_theta > 180){
		p_theta = -180 + (p_theta - 180);
// 		SDL_WarpMouseInWindow(p_windowId, midWidth + p_theta/coefSensibiliteX , midHeight + p_phi/coefSensibiliteY);
	}
// 	en fait on à fait les calculs à l'envers alors on change theta
	p_theta = -p_theta;
}

///définition de l'operateur = pour la caméra
/**	@param camera : camera à copier
	@return caméra copiée
*/
SimpleCameraGl3 & SimpleCameraGl3::operator = (const SimpleCameraGl3 & camera){
	this->copyCamera(camera);
	return *this;
}


///fonction de copie de la caméra
/**	@param camera : camera à copier
*/
void SimpleCameraGl3::copyCamera(const SimpleCameraGl3 & camera){
	p_position = camera.p_position;
	p_theta = camera.p_theta;
	p_phi = camera.p_phi;
	p_rouli = camera.p_rouli;
	p_up = camera.p_up;
	p_visee = camera.p_visee;
	p_planGauche = camera.p_planGauche;
	p_windowId = camera.p_windowId;
}

///Fonction qui initialise la caméra
/**	@param position : position de la caméra
	@param theta : angle de la caméra dans le plan xOy avec l'axe Ox
	@param phi : angle de la caméra dans le plan xOz avec l'axe Oz
	@param rouli : angle de la caméra dans le plan yOz avec l'axe Oz
*/
void SimpleCameraGl3::initialisation(const Vecteur3f& position, float theta, float phi, float rouli){
	p_position = position;
	p_theta = theta;
	p_phi = phi;
	p_rouli = rouli;
	p_windowId = NULL;
	updateCameraGl3();
}

///Fonction qui recalcul les Vecteurs de la caméra par rapport aux angles théta, phi et rouli
void SimpleCameraGl3::updateCameraGl3(){
	p_visee.setXYZ(cos(p_theta)*sin(p_phi), sin(p_theta)*sin(p_phi), cos(p_phi)); //le Vecteur de visée est bon
	p_planGauche.setXYZ(-sin(p_theta)*sin(p_rouli), cos(p_theta)*sin(p_rouli), cos(p_rouli));//Vecteur unit bon (avec p_rouli = M_PI/2.0 par défaut
// 	p_up.setXYZ(cos(p_theta)*cos(p_phi), sin(p_theta)*cos(p_phi), -sin(p_phi));   //vecteur up de base, dérivée de visée par rapport à phi
// 	p_up.setXYZ(-cos(p_theta)*cos(p_phi), -sin(p_theta)*cos(p_phi), +sin(p_phi));  //vecteur -up
	p_up = (p_visee^p_planGauche).getUnit();
	
// 	cout << p_visee << "\t" << p_planGauche << "\t" << p_up << endl;
// 	cout << p_visee*p_planGauche << "\t" << p_planGauche*p_up << "\t" << p_up*p_visee << endl;
}


