#include <cmath>
#include <GL/glut.h>
#include <list>
#include <vector>

#include "globale.h"
#include "Temps.h"
#include "CVector3.h"
#include "CCamera.h"
#include "CCube.h"
#include "CBloc.h"
#include "Mouvement.h"
#include "CJeu.h"




// Cette fonction sert, pendant le jeu, à "faire regarder" la caméra du bon point, et
// dans la bonne direction selon le mode qui est choisi.

void CCamera::CamLookAt(void)
{
	CVector3 Cible;

	switch (ModeCam)
	{
	case (CENTRE) : Cible = CVector3(MAX_X2, MAX_Y2, MAX_Z2); break;
	case (SUITBLOC) : Cible = CVector3(gJeu.Bloc.x + gJeu.Bloc.offX,
									   gJeu.Bloc.y + gJeu.Bloc.offY,
									   gJeu.Bloc.z + gJeu.Bloc.offZ); break;
	case (SUITGUIDE) : Cible = CVector3(gJeu.BlocGuide.x + gJeu.BlocGuide.offX,
										gJeu.BlocGuide.y + gJeu.BlocGuide.offY,
										gJeu.BlocGuide.z + gJeu.BlocGuide.offZ); break;
	}

	Observation += (Cible - Observation) * SPEEDOBSERVCAM;

	gluLookAt(Position.x, Position.y, Position.z,
			  Observation.x, Observation.y, Observation.z,
			  gCamera.Haut.x, gCamera.Haut.y, gCamera.Haut.z);
}




// Fonction qui change le mode de la caméra
void CCamera::ChangeMode(void)
{
	ModeCam = type_modecam( (ModeCam + 1) % 3 );
}




// Fonction qui vérifie que la position de la caméra est valide

inline bool In(const CVector3& Point)
{
	switch (GetDirLookAt())
	{
	case AVANT: if (Point.y - gCamera.Observation.y > 0) return (true); break;
	case DROITE: if (Point.x - gCamera.Observation.x > 0) return (true); break;
	case ARRIERE: if (Point.y - gCamera.Observation.y < 0) return (true); break;
	case GAUCHE: if (Point.x - gCamera.Observation.x < 0) return (true); break;
	}

	if (Point.z < 0)
		return (true);
	if (Point.z > MAX_Z)
		return (false);
	
	if ( (Point.x > 0) && (Point.x < MAX_X) && (Point.y > 0) && (Point.y < MAX_Y) )
		return (true);

	return (false);
}




// Fait tourner la caméra autour d'un axe qui passe par le point d'observation

void CCamera::TournePosition(float Angle, const CVector3& Axe)
{
	CVector3 NouvPos;
	CVector3 Temp = Position - Observation;
	
	float LeCos = cos(Angle);
	float LeSin = sin(Angle);

	// Rotation à l'aide de multiplication de matrices

	NouvPos.x  = (LeCos + (1 - LeCos) * Axe.x * Axe.x)			* Temp.x;
	NouvPos.x += ((1 - LeCos) * Axe.x * Axe.y - Axe.z * LeSin)	* Temp.y;
	NouvPos.x += ((1 - LeCos) * Axe.x * Axe.z + Axe.y * LeSin)	* Temp.z;

	NouvPos.y  = ((1 - LeCos) * Axe.x * Axe.y + Axe.z * LeSin)	* Temp.x;
	NouvPos.y += (LeCos + (1 - LeCos) * Axe.y * Axe.y)			* Temp.y;
	NouvPos.y += ((1 - LeCos) * Axe.y * Axe.z - Axe.x * LeSin)	* Temp.z;

	NouvPos.z  = ((1 - LeCos) * Axe.x * Axe.z - Axe.y * LeSin)	* Temp.x;
	NouvPos.z += ((1 - LeCos) * Axe.y * Axe.z + Axe.x * LeSin)	* Temp.y;
	NouvPos.z += (LeCos + (1 - LeCos) * Axe.z * Axe.z)			* Temp.z;


	if (!In(NouvPos + Observation))
		Position = Observation + NouvPos;
}




// Fait avancer la caméra dans la direction où elle regarde

void CCamera::Avance(float Delta)
{
	CVector3 Direction = Observation - Position;
	CVector3 NouvPos = Position + Direction / Direction.Norm() * Delta *
					   (GetTemps() - gCamera.TempsLastFrame);

	if (!In(NouvPos))
		Position = NouvPos;
}
