/*****************************************************************************
 *
 *  Copyright (C) 2003 Cédric Brégardis <cedric.bregardis@free.fr>
 *
 *  This file is part of BRIQUOLO
 *
 *  BRIQUOLO is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  BRIQUOLO is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with BRIQUOLO; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *****************************************************************************/
#include "../I18n.h"
#include "MOGL_Camera.h"
#include "MOGL_Temps.h"
#include <iostream>

using namespace std;

#ifndef M_PI
#define M_PI 3.14159265359
#endif


MOGL_Camera::MOGL_Camera() :  _AngleHaut(0), _AngleInclinaison(0), _ClippingMin(5), _ClippingMax(5000), _Rapport(4.0f/3.0f), _Fov(45)
{
  _Position.x=0;
  _Position.y=0;
  _Position.z=0;

  _Haut.x=0;
  _Haut.y=1;
  _Haut.z=0;
    
  _DroiteInit.x=1;
  _DroiteInit.y=0;
  _DroiteInit.z=0;
}

void MOGL_Camera::EnvoyerAOpenGL()
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(_Fov, _Rapport, _ClippingMin, _ClippingMax);
  glMatrixMode(GL_MODELVIEW);

  MOGL_Struct_Vecteur direction=GetDirection();

  gluLookAt(_Position.x, _Position.y, _Position.z,
            _Position.x+direction.x, _Position.y+direction.y, _Position.z+direction.z,
            _Haut.x, _Haut.y, _Haut.z);
}

void MOGL_Camera::SetPosition(GLfloat p_X,GLfloat p_Y,GLfloat p_Z)
{
  _Position.x=p_X;
  _Position.y=p_Y;
  _Position.z=p_Z;
}

void MOGL_Camera::SetPosition(MOGL_Struct_Vecteur p_Vecteur)
{
  _Position=p_Vecteur;
}

MOGL_Struct_Vecteur MOGL_Camera::GetPosition()
{
  return _Position;
}

void MOGL_Camera::SetDirection(GLfloat p_X,GLfloat p_Y,GLfloat p_Z)
{
  float angle=acos(_Haut.x*p_X + _Haut.y*p_Y + _Haut.z*p_Z);
  _AngleInclinaison=(M_PI/2-angle)*180/M_PI;

  
  MOGL_Struct_Vecteur dir;
  dir.x=p_X;
  dir.y=p_Y;
  dir.z=p_Z;
  MOGL_Struct_Vecteur droite=dir.ProduitVectoriel(_Haut);

  float norm=sqrt(droite.x*droite.x+droite.y*droite.y+droite.z*droite.z);
  if (norm != 0)
  {
    droite.x /= norm;
    droite.y /= norm;
    droite.z /= norm;
  }
  
  _AngleHaut=acos(_DroiteInit.x*droite.x + _DroiteInit.y*droite.y + _DroiteInit.z*droite.z)*180/M_PI;

  MOGL_Struct_Vecteur droiteTest=GetDroite();

  float epsilon=0.0001f;
  

  if (!(fabsf(droiteTest.x-droite.x)<epsilon && fabsf(droiteTest.y-droite.y)<epsilon && fabsf(droiteTest.z-droite.z)<epsilon))
  {
    _AngleHaut=-_AngleHaut;
    
  }
  
  
}

void MOGL_Camera::SetDirection(MOGL_Struct_Vecteur p_Vecteur)
{
  SetDirection(p_Vecteur.x, p_Vecteur.y, p_Vecteur.z);
}


MOGL_Struct_Vecteur MOGL_Camera::GetDirection()
{
  MOGL_MatriceTransformation matRotHaut=MOGL_MatriceTransformation::FabriqueRotation(_Haut, _AngleHaut);
  MOGL_MatriceTransformation matRotInclinaison=MOGL_MatriceTransformation::FabriqueRotation(_DroiteInit,_AngleInclinaison);

  MOGL_MatriceTransformation matRot=matRotInclinaison;
  matRot.Multiplier(matRotHaut);

  MOGL_Struct_Vecteur dirInit=_Haut.ProduitVectoriel(_DroiteInit);
  MOGL_Struct_Vecteur direction=matRot.MultiplierVecteur(dirInit);

  return direction;
}

MOGL_Struct_Vecteur MOGL_Camera::GetDroite()
{
  MOGL_MatriceTransformation matRotHaut=MOGL_MatriceTransformation::FabriqueRotation(_Haut, _AngleHaut);

  MOGL_Struct_Vecteur droite=matRotHaut.MultiplierVecteur(_DroiteInit);
  
  return droite;
}


void MOGL_Camera::SetHaut(GLfloat p_X,GLfloat p_Y,GLfloat p_Z)
{
  MOGL_Struct_Vecteur ancienneDirection=GetDirection();
  
  float norm=sqrt(p_X*p_X+p_Y*p_Y+p_Z*p_Z);

  if (norm!=0)
  {
    _Haut.x=p_X/norm;
    _Haut.y=p_Y/norm;
    _Haut.z=p_Z/norm;
  }
  else
  {
    cerr<<"Position of the camera is invalid (0,0,0) - Using the standard up position (0,1,0)"<<endl;
    _Haut.x=0;
    _Haut.y=1;
    _Haut.z=0;
    
    _DroiteInit.x=1;
    _DroiteInit.y=0;
    _DroiteInit.z=0;
  }

  if (_Haut.x!=0)
  {
    _DroiteInit.x=-(_Haut.y + _Haut.z)/_Haut.x;
    _DroiteInit.y=1;
    _DroiteInit.z=1;
  }
  else if (_Haut.y!=0)
  {
    _DroiteInit.x=1;
    _DroiteInit.y=-(_Haut.x + _Haut.z)/_Haut.y;
    _DroiteInit.z=1;
  }
  else if (_Haut.z!=0)
  {
    _DroiteInit.x=1;
    _DroiteInit.y=1;
    _DroiteInit.z=-(_Haut.x + _Haut.y)/_Haut.z;
  }

  norm=sqrt(_DroiteInit.x*_DroiteInit.x+_DroiteInit.y*_DroiteInit.y+_DroiteInit.z*_DroiteInit.z);
  _DroiteInit.x/=norm;
  _DroiteInit.y/=norm;
  _DroiteInit.z/=norm;

  SetDirection(ancienneDirection);
}

void MOGL_Camera::SetHaut3DS()
{
  SetHaut(0,0,1);
}

void MOGL_Camera::SetHautBlender()
{
  SetHaut(0,0,1);
}

void MOGL_Camera::SetHautMilkshape()
{
  SetHaut(0,1,0);
}

void MOGL_Camera::SetHaut(MOGL_Struct_Vecteur p_Vecteur)
{
  SetHaut(p_Vecteur.x, p_Vecteur.y, p_Vecteur.z);
}

MOGL_Struct_Vecteur MOGL_Camera::GetHaut()
{
  return _Haut;
}

void MOGL_Camera::AvancerVitesse(float p_Vitesse)
{
  MOGL_Struct_Vecteur direction=GetDirection();
  direction.x*=p_Vitesse;
  direction.y*=p_Vitesse;
  direction.z*=p_Vitesse;
  MOGL_Struct_Vecteur deplacement=MOGL_Temps::GetVariationVecteurDistance(direction);

  _Position.x+=deplacement.x;
  _Position.y+=deplacement.y;
  _Position.z+=deplacement.z;
}

void MOGL_Camera::ReculerVitesse(float p_Vitesse)
{
  MOGL_Struct_Vecteur direction=GetDirection();
  direction.x*=p_Vitesse;
  direction.y*=p_Vitesse;
  direction.z*=p_Vitesse;
  MOGL_Struct_Vecteur deplacement=MOGL_Temps::GetVariationVecteurDistance(direction);

  _Position.x-=deplacement.x;
  _Position.y-=deplacement.y;
  _Position.z-=deplacement.z;
}


void MOGL_Camera::Avancer(float p_Distance)
{
  MOGL_Struct_Vecteur deplacement=GetDirection();
  deplacement.x*=p_Distance;
  deplacement.y*=p_Distance;
  deplacement.z*=p_Distance;

  _Position.x+=deplacement.x;
  _Position.y+=deplacement.y;
  _Position.z+=deplacement.z;
}

void MOGL_Camera::Reculer(float p_Distance)
{
  MOGL_Struct_Vecteur deplacement=GetDirection();
  deplacement.x*=p_Distance;
  deplacement.y*=p_Distance;
  deplacement.z*=p_Distance;

  _Position.x-=deplacement.x;
  _Position.y-=deplacement.y;
  _Position.z-=deplacement.z;
}

void MOGL_Camera::Droite(float p_Distance)
{
  MOGL_Struct_Vecteur deplacement=GetDroite();
  deplacement.x*=p_Distance;
  deplacement.y*=p_Distance;
  deplacement.z*=p_Distance;

  _Position.x+=deplacement.x;
  _Position.y+=deplacement.y;
  _Position.z+=deplacement.z;
}

void MOGL_Camera::Gauche(float p_Distance)
{
  MOGL_Struct_Vecteur deplacement=GetDroite();
  deplacement.x*=p_Distance;
  deplacement.y*=p_Distance;
  deplacement.z*=p_Distance;

  _Position.x-=deplacement.x;
  _Position.y-=deplacement.y;
  _Position.z-=deplacement.z;
}


void MOGL_Camera::DroiteVitesse(float p_Vitesse)
{
  MOGL_Struct_Vecteur direction=GetDroite();
  direction.x*=p_Vitesse;
  direction.y*=p_Vitesse;
  direction.z*=p_Vitesse;
  MOGL_Struct_Vecteur deplacement=MOGL_Temps::GetVariationVecteurDistance(direction);

  _Position.x+=deplacement.x;
  _Position.y+=deplacement.y;
  _Position.z+=deplacement.z;
}

void MOGL_Camera::GaucheVitesse(float p_Vitesse)
{
  MOGL_Struct_Vecteur direction=GetDroite();
  direction.x*=p_Vitesse;
  direction.y*=p_Vitesse;
  direction.z*=p_Vitesse;
  MOGL_Struct_Vecteur deplacement=MOGL_Temps::GetVariationVecteurDistance(direction);

  _Position.x-=deplacement.x;
  _Position.y-=deplacement.y;
  _Position.z-=deplacement.z;
}

void MOGL_Camera::TournerDroiteVitesse(float p_Vitesse)
{
  _AngleHaut+=MOGL_Temps::GetVariationAngle(p_Vitesse);

  if (_AngleHaut>360)
  {
    _AngleHaut-=360;
  }
}

void MOGL_Camera::TournerGaucheVitesse(float p_Vitesse)
{
  _AngleHaut-=MOGL_Temps::GetVariationAngle(p_Vitesse);
  if (_AngleHaut<0)
  {
    _AngleHaut+=360;
  }
}

void MOGL_Camera::TournerDroite(float p_Angle)
{
  _AngleHaut+=p_Angle;
  if (_AngleHaut>360)
  {
    _AngleHaut-=360;
  }

}

void MOGL_Camera::TournerGauche(float p_Angle)
{
  _AngleHaut-=p_Angle;
  if (_AngleHaut<0)
  {
    _AngleHaut+=360;
  }

}

void MOGL_Camera::TournerHaut(float p_Angle)
{
  _AngleInclinaison+=p_Angle;
  if (_AngleInclinaison>90)
  {
    _AngleInclinaison=90;
  }
}

void MOGL_Camera::TournerBas(float p_Angle)
{
  _AngleInclinaison-=p_Angle;
  if (_AngleInclinaison<-90)
  {
    _AngleInclinaison=-90;
  }

}

void MOGL_Camera::SetClipping(float p_Min, float p_Max)
{
        _ClippingMin=p_Min;
        _ClippingMax=p_Max;
}

void MOGL_Camera::SetRaport(float p_Rapport)
{
  _Rapport=p_Rapport;
}

float MOGL_Camera::GetClippingMin()
{
  return _ClippingMin;
}

float MOGL_Camera::GetClippingMax()
{
  return _ClippingMax;
}

float MOGL_Camera::GetFov() const
{
  return _Fov;
}

void MOGL_Camera::SetFov(float p_Fov)
{
  _Fov=p_Fov;
}

