/*****************************************************************************
 *
 *  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_EnsembleObjet.h"
#include "MOGL_Armature.h"
#include "MOGL_Peau.h"
//#include
#include <stdio.h>

//#include <mmsystem.h>

MOGL_EnsembleObjet::MOGL_EnsembleObjet()
{
}

bool MOGL_EnsembleObjet::Charger(char * p_NomFichier, const MOGL_GestionnaireTexture p_GM)
{
  char  chaine[200];
  int   i,j;
  char  temp[50];
  float x,y,z,u,v,xn,yn,zn;
  int index_point=0;
  bool debut=true;
  bool PremiereFace=true;
  bool Noeud=false;
  MOGL_Struct_Point Pt[3];
  MOGL_Texture * Texture=NULL;
  MOGL_Noeud * ObjetCourant;
  string NomCourant;
  unsigned int NiveauCourant=0;

  vector <MOGL_Noeud *> TabObjet;
  _Noeud=new MOGL_Noeud;
  TabObjet.push_back(_Noeud);

  ifstream Fichier(p_NomFichier);

  if (Fichier.fail())
  {
        return false;
  }

  do
  {
        Fichier.getline(chaine,200);

        if (!strncmp(chaine,"Objet",5))
        {
          // C'est un objet avec des faces


          if (!debut)
          {
                if (!Noeud)
                {
                  ((MOGL_Objet *)ObjetCourant)->AjouterTriangle(Pt[0],Pt[1],Pt[2],Texture);
                }
                index_point=0;
                _MapObjet[NomCourant]=ObjetCourant;
                if (TabObjet.size()<=NiveauCourant)
                {
                  TabObjet.push_back(ObjetCourant);
                }
                {
                  TabObjet[NiveauCourant]=ObjetCourant;
                }
                TabObjet[NiveauCourant-1]->AjouterElement(ObjetCourant);
          }
          else
          {
                debut=false;
          }
          ObjetCourant=new MOGL_Objet;
          PremiereFace=true;

          // Quelle est le nom ?

          i=0;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          NomCourant=string(temp);
          Noeud=false;
        }
        if (!strncmp(chaine,"Noeud",5))
        {
          // C'est un simple noeud sans faces


          if (!debut)
          {
                if (!Noeud)
                {
                  ((MOGL_Objet *)ObjetCourant)->AjouterTriangle(Pt[0],Pt[1],Pt[2],Texture);
                }
                index_point=0;
                _MapObjet[NomCourant]=ObjetCourant;
                if (TabObjet.size()<=NiveauCourant)
                {
                  TabObjet.push_back(ObjetCourant);
                }
                {
                  TabObjet[NiveauCourant]=ObjetCourant;
                }
                TabObjet[NiveauCourant-1]->AjouterElement(ObjetCourant);

          }
          else
          {
                debut=false;
          }
          ObjetCourant=new MOGL_Noeud;
          PremiereFace=true;

          // Quelle est le nom ?

          i=0;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          NomCourant=string(temp);
          Noeud=true;
        }

        if (!strncmp(chaine,"Niveau",6))
        {
          i=0;
          while(chaine[i]!=':') i++;
          i++;
          sscanf(chaine+i,"%i",&NiveauCourant);
        }


        if (!strncmp(chaine,"FACE",4))
        {
          if (!PremiereFace)
          {
                ((MOGL_Objet *)ObjetCourant)->AjouterTriangle(Pt[0],Pt[1],Pt[2],Texture);
          }
          else
          {
                PremiereFace=false;
          }
          index_point=0;
        }
        if (!strncmp(chaine,"Material",8))
        {
          // Quelle est la texture ?
          i=0;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          Texture=p_GM.GetTexture(temp);

        }

        if (!strncmp(chaine,"Vertex",6))
        {       // Lecture des coordonnées d'un point

          i=0;

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          while(chaine[i]!='U') i++;
          i+=2;
          sscanf(chaine+i,"%f",&u);

          while(chaine[i]!='V') i++;
          i+=2;
          sscanf(chaine+i,"%f",&v);

          while(chaine[i]!='x') i++;
          i+=2;
          sscanf(chaine+i,"%f",&xn);

          while(chaine[i]!='y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&yn);

          while(chaine[i]!='z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&zn);

          Pt[index_point].xp=x;
          Pt[index_point].yp=y;
          Pt[index_point].zp=z;

          Pt[index_point].xn=xn;
          Pt[index_point].yn=yn;
          Pt[index_point].zn=zn;

          Pt[index_point].xt=u;
          Pt[index_point].yt=v;

          index_point++;
        }
  } while(!Fichier.eof());
  if (!Noeud)
  {
        ((MOGL_Objet *)ObjetCourant)->AjouterTriangle(Pt[0],Pt[1],Pt[2],Texture);
  }
  _MapObjet[NomCourant]=ObjetCourant;
  TabObjet[NiveauCourant-1]->AjouterElement(ObjetCourant);

  return true;
}

bool MOGL_EnsembleObjet::ChargerArmaturePeau(char * p_NomFichier, const MOGL_GestionnaireTexture p_GM)
{
  typedef enum{ARMATURE, NOEUD, PEAU} Enum_TypeObjet;
  Enum_TypeObjet type_objet=NOEUD;
  char  chaine[200];
  int   i,j;
  char  temp[50];
  float x,y,z,u,v,xn,yn,zn;
  unsigned int indicePoint;
  unsigned int numPoint=0;
  int index_point=0;
  bool debut=true;
  bool PremiereFace=true;
  unsigned int PtNum[3];

  MOGL_Texture * Texture=NULL;
  MOGL_Noeud * ObjetCourant;
  MOGL_TrianglePeau Triangle;
  string NomCourant,NomRef;
  unsigned int NiveauCourant=0;
  MOGL_Struct_Vecteur Rotation = {0,0,0};
  MOGL_Struct_Vecteur Translation = {0,0,0};

  vector <MOGL_Noeud *> TabObjet;

  bool peauRencontre=false;


  // Ici le _Noeud de base est un MOGL_Peau
  _Noeud=new MOGL_Peau;

  TabObjet.push_back(_Noeud);


  ifstream Fichier(p_NomFichier);

  if (Fichier.fail())
  {
        return false;
  }

  do
  {
        Fichier.getline(chaine,200);

        if (!strncmp(chaine,"Objet",5))
        {
          if (!debut)
          {
                if (type_objet==PEAU && !PremiereFace)
                {
                  static_cast<MOGL_Peau *>(ObjetCourant)->AjouterTriangle(Triangle);
                }

                index_point=0;
                _MapObjet[NomCourant]=ObjetCourant;
                if (type_objet==ARMATURE)
                {
                  MOGL_MatriceTransformation Mat=MOGL_MatriceTransformation::FabriqueRotation(Rotation.x, Rotation.y, Rotation.z);
                  Mat.AjouterTranslation(Translation.x, Translation.y, Translation.z);
                  ((MOGL_Armature *)ObjetCourant)->SetMatriceInitialisation(Mat);
                  ((MOGL_Armature *)ObjetCourant)->ReinitialiserPosition();
                  static_cast<MOGL_Peau*>(_Noeud)->DefinirInvMatriceBase(static_cast<MOGL_Armature*>(ObjetCourant));
                }
                if (type_objet!=PEAU)
                {
                  if (TabObjet.size()<=NiveauCourant)
                  {
                        TabObjet.push_back(ObjetCourant);
                  }
                  {
                        TabObjet[NiveauCourant]=ObjetCourant;
                  }
                  TabObjet[NiveauCourant-1]->AjouterElement(ObjetCourant);
                }
          }
          else
          {
                debut=false;
          }
          //PremiereFace=true;

          // Quelle est le nom ?

          i=5;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          NomCourant=string(temp);
          numPoint=0;
        }
        if (!strncmp(chaine,"Niveau",6))
        {
          i=6;
          while(chaine[i]!=':') i++;
          i++;
          sscanf(chaine+i,"%i",&NiveauCourant);
        }

        if (!strncmp(chaine,"Bone",4))
        {
          ObjetCourant=new MOGL_Armature();
          type_objet=ARMATURE;
        }

        if (!strncmp(chaine,"Skin",4))
        {
          if (!peauRencontre)
          {
                ObjetCourant=_Noeud;
                type_objet=PEAU;
                PremiereFace=true;
                peauRencontre=true;
          }
          else
          {
                cerr<<"The file contains two skin objects, which is impossinble!"<<endl;
                return false;
          }
        }

        if (!strncmp(chaine,"Noeud",5))
        {
          ObjetCourant=new MOGL_Noeud();
          type_objet=NOEUD;
        }

        if (!strncmp(chaine,"Rotation",8))
        {
          // Lecture de la rotation
          i=8;

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          Rotation.x=x;
          Rotation.y=y;
          Rotation.z=z;
        }
        if (!strncmp(chaine,"Translation",11))
        {
          // Lecture de la translation
          i=11;

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          Translation.x=x;
          Translation.y=y;
          Translation.z=z;
        }

        if (!strncmp(chaine,"POINT",5))
        {
          // Lecture des coordonnées d'un point
          // pour un objet normal
          i=5;

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          while(chaine[i]!='x') i++;
          i+=2;
          sscanf(chaine+i,"%f",&xn);

          while(chaine[i]!='y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&yn);

          while(chaine[i]!='z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&zn);

          switch (type_objet)
          {
                case ARMATURE :
                {
                  static_cast<MOGL_Armature*>(ObjetCourant)->AjouterPoint(numPoint,x,y,z,xn,yn,zn);
                  numPoint++;
                  break;
                }
                case PEAU :
                {
                  static_cast<MOGL_Peau*>(ObjetCourant)->AjouterPoint(numPoint,x,y,z,xn,yn,zn);
                  numPoint++;
                  break;
                }
                case NOEUD :
                {
                  cerr<<"A node can't contain points!"<<endl;
                }
          }
        }

        if (!strncmp(chaine,"FACE",4))
        {
          if (!PremiereFace)
          {
                static_cast<MOGL_Peau *>(ObjetCourant)->AjouterTriangle(Triangle);
          }
          else
          {
                PremiereFace=false;
          }
          index_point=0;
          Triangle.Reinitialiser();
        }

        if (!strncmp(chaine,"Material",8))
        {
          // Quelle est la texture ?
          i=8;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          Texture=p_GM.GetTexture(temp);
          if (Texture==NULL)
          {
                cerr<<"Texture not found"<<endl;
          }
          Triangle.SetTexture(Texture);
        }

        if (!strncmp(chaine,"Vertex",6))
        {
          // Lecture des coordonnées d'un point
          i=6;

          while(chaine[i]!='P') i++;
          i++;
          int indice_ref;

          if (chaine[i]=='E')
          {
                // C'est une référence externe
                i+=2;
                sscanf(chaine+i,"%i",&indice_ref);

                while(chaine[i]!='"') i++;
                i++;
                j=i;
                while(chaine[j]!='"') j++;
                strncpy(temp,chaine+i,j-i);
                temp[j-i]=0;

                Triangle.SetPoint(index_point, (MOGL_Armature *)_MapObjet[temp], indice_ref-1);
          }
          else
          {
                // C'est une référence interne
                i+=2;
                sscanf(chaine+i,"%i",&indice_ref);

                Triangle.SetPoint(index_point, indice_ref-1);
          }

          i+=2;
          sscanf(chaine+i,"%i",&indicePoint);

          while(chaine[i]!='U') i++;
          i+=2;
          sscanf(chaine+i,"%f",&u);

          while(chaine[i]!='V') i++;
          i+=2;
          sscanf(chaine+i,"%f",&v);

          PtNum[index_point]=indicePoint-1;

          MOGL_Struct_PointTexture PtText;
          PtText.xt=u;
          PtText.yt=v;
          Triangle.SetPointTexture(index_point, PtText);

          index_point++;
        }

  } while(!Fichier.eof());

  if (!peauRencontre)
  {
        cerr<<"The file doesn't contain a skin! That's impossible!"<<endl;
        return false;
  }

  if (type_objet==PEAU && !PremiereFace)
  {
        static_cast<MOGL_Peau *>(ObjetCourant)->AjouterTriangle(Triangle);
  }

  if (type_objet==ARMATURE)
  {
        MOGL_MatriceTransformation Mat=MOGL_MatriceTransformation::FabriqueRotation(Rotation.x, Rotation.y, Rotation.z);
        Mat.AjouterTranslation(Translation.x, Translation.y, Translation.z);
        ((MOGL_Armature *)ObjetCourant)->SetMatriceInitialisation(Mat);
        ((MOGL_Armature *)ObjetCourant)->ReinitialiserPosition();
        static_cast<MOGL_Peau*>(_Noeud)->DefinirInvMatriceBase(static_cast<MOGL_Armature*>(ObjetCourant));

  }
  if (type_objet!=PEAU)
  {
        _MapObjet[NomCourant]=ObjetCourant;
        TabObjet[NiveauCourant-1]->AjouterElement(ObjetCourant);
  }


  return true;
}


bool MOGL_EnsembleObjet::ChargerAnimation(char * p_NomFichier, char * p_NomAnimation)
{
  char  chaine[200];
  int   i,j;
  char  temp[50];
  //float x,y,z,u,v,xn,yn,zn;
  float t,x,y,z;
  int index_point=0;
  bool debut=true;
  MOGL_Noeud * NoeudCourant;
  MOGL_Animation * AnimationCourant=NULL;
  MOGL_GroupeAnimation * GroupeAnimation=new MOGL_GroupeAnimation;
  string NomCourant;


  ifstream Fichier(p_NomFichier);

  if (Fichier.fail())
  {
        return false;
  }

  do
  {
        Fichier.getline(chaine,200);

        if (!strncmp(chaine,"Nom",3))
        {
          // Quelle est le nom ?

          if (!debut)
          {
                GroupeAnimation->AjouterAnimation(AnimationCourant);
          }
          else
          {
                debut=false;
          }

          //PremiereFace=true;
          i=0;
          while(chaine[i]!='"') i++;
          i++;
          j=i;
          while(chaine[j]!='"') j++;
          strncpy(temp,chaine+i,j-i);
          temp[j-i]=0;
          NomCourant=string(temp);
          NoeudCourant=_MapObjet[NomCourant];
          AnimationCourant=new MOGL_Animation(NoeudCourant);
        }



        if (!strncmp(chaine,"Rotation",8))
        {
          // Lecture de la rotation
          i=8;

          while(chaine[i]!='T') i++;
          i+=2;
          sscanf(chaine+i,"%f",&t);

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          index_point++;

          MOGL_Struct_Vecteur vect;
          vect.x=x;
          vect.y=y;
          vect.z=z;

          AnimationCourant->AjouterClefRotation(t,vect);
        }
        if (!strncmp(chaine,"Translation",11))
        {
          // Lecture de la translation
          i=11;

          while(chaine[i]!='T') i++;
          i+=2;
          sscanf(chaine+i,"%f",&t);

          while(chaine[i]!='X') i++;
          i+=2;
          sscanf(chaine+i,"%f",&x);

          while(chaine[i]!='Y') i++;
          i+=2;
          sscanf(chaine+i,"%f",&y);

          while(chaine[i]!='Z') i++;
          i+=2;
          sscanf(chaine+i,"%f",&z);

          index_point++;

          MOGL_Struct_Vecteur vect;
          vect.x=x;
          vect.y=y;
          vect.z=z;

          AnimationCourant->AjouterClefTranslation(t,vect);
        }
  } while(!Fichier.eof());
  GroupeAnimation->AjouterAnimation(AnimationCourant);

  _MapGroupeAnimation[p_NomAnimation]=GroupeAnimation;

  return true;
}


void MOGL_EnsembleObjet::DebuterAnimation(char * p_NomAnim)
{
  MOGL_ItMap_GroupeAnimation itGAnim=_MapGroupeAnimation.find(p_NomAnim);
  if (itGAnim!=_MapGroupeAnimation.end())
  {
        _ListeAnimCourante.insert(p_NomAnim);
        itGAnim->second->SetUniteTemps(0.5);
        itGAnim->second->Initialiser();
  }

}

void MOGL_EnsembleObjet::Animer()
{
  for(MOGL_ItSet_String it=_ListeAnimCourante.begin(); it!=_ListeAnimCourante.end(); it++)
  {
        MOGL_ItMap_GroupeAnimation itGAnim=_MapGroupeAnimation.find(*it);
        if (itGAnim!=_MapGroupeAnimation.end())
        {
          if (!itGAnim->second->SetTemps())
          {
                itGAnim->second->Initialiser();
          }
        }
  }
}

MOGL_Noeud * MOGL_EnsembleObjet::GetNoeudPrincipal()
{
  return _Noeud;
}

MOGL_Noeud * MOGL_EnsembleObjet::GetNoeud(char * p_Nom)
{
  MOGL_ItMap_Noeud it;
  it=_MapObjet.find(p_Nom);
  if (it!=_MapObjet.end())
        return it->second;
  else
        return NULL;
}
