/*****************************************************************************
 *
 *  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 "MOGL_Peau.h"

MOGL_Peau::MOGL_Peau(): MOGL_Noeud(), _ModifieTriangle(true)
{
}

MOGL_Peau::~MOGL_Peau()
{
}

void MOGL_Peau::AjouterPoint(unsigned int p_NumPoint, const MOGL_Struct_PointSimple & p_Point)
{
        if (_VecteurPoint.size()<=p_NumPoint)
        {
                _VecteurPoint.resize(p_NumPoint+1);
        }
        _VecteurPoint[p_NumPoint]=p_Point;
}

void MOGL_Peau::AjouterPoint(unsigned int p_NumPoint, GLfloat xp, GLfloat yp, GLfloat zp,
                                                                                GLfloat xn, GLfloat yn, GLfloat zn)

{
        MOGL_Struct_PointSimple Pt;
        Pt.xp=xp;
        Pt.yp=yp;
        Pt.zp=zp;
        Pt.xn=xn;
        Pt.yn=yn;
        Pt.zn=zn;
        if (_VecteurPoint.size()<=p_NumPoint)
        {
                _VecteurPoint.resize(p_NumPoint+1);
                //_VecteurPointTransforme.resize(p_NumPoint+1);
        }
        _VecteurPoint[p_NumPoint]=Pt;
}

void MOGL_Peau::AjouterTriangle(const MOGL_TrianglePeau & p_TrianglePeau)
{
  _ListeTrianglePeau.push_back(p_TrianglePeau);
  _ModifieTriangle=true;
}

void MOGL_Peau::CalculerPoints()
{

  MOGL_ItVecteur_PointSimple itPoint;
  unsigned int num=0;
  for(itPoint=_VecteurPoint.begin();itPoint!=_VecteurPoint.end();itPoint++)
  {
        MOGL_Struct_Vecteur vec,pt,normal;
        vec.x=itPoint->xp;
        vec.y=itPoint->yp;
        vec.z=itPoint->zp;
        pt=_MatriceFinale.MultiplierVecteur(vec);

        vec.x=itPoint->xn;
        vec.y=itPoint->yn;
        vec.z=itPoint->zn;
                
        normal=_MatriceFinale.EffectuerRotationVecteur(vec);
                
        MOGL_Struct_PointSimple ptSimple;
        ptSimple.xp=pt.x;
        ptSimple.yp=pt.y;
        ptSimple.zp=pt.z;
        ptSimple.xn=normal.x;
        ptSimple.yn=normal.y;
        ptSimple.zn=normal.z;
        _VecteurPointTransforme[num]=ptSimple;
        num++;
  }

  MOGL_ItListe_TrianglePeau itTriPeau;
  MOGL_Struct_AffichageTriangle Triangle;
  MOGL_Struct_PointSimple PointTr;
  unsigned int indice=0;
  for(itTriPeau=_ListeTrianglePeau.begin(); itTriPeau!=_ListeTrianglePeau.end();itTriPeau++)
  {     
        for(int i=0;i<3;i++)
        {
          if (itTriPeau->EstPointInterne(i))
          {
                PointTr=_VecteurPointTransforme[itTriPeau->GetReferencePoint(i)];
          }
          else
          {
                PointTr=itTriPeau->GetArmaturePoint(i)->GetPointTransforme(itTriPeau->GetReferencePoint(i));
          }
          
          Triangle=_TabAffichageTriangle[indice];
          *(Triangle.normal+3*i+0)=PointTr.xn;
          *(Triangle.normal+3*i+1)=PointTr.yn;
          *(Triangle.normal+3*i+2)=PointTr.zn;

          *(Triangle.point+3*i+0)=PointTr.xp;
          *(Triangle.point+3*i+1)=PointTr.yp;
          *(Triangle.point+3*i+2)=PointTr.zp;

          // Les coordonnées de texture n'ont besoin d'être écrite que lorsque
          // Les tableaux ont été modifiés, par lorsque la matrice l'a été
          if (_ModifieTriangle)
          {
                *(Triangle.texture+2*i+0)=itTriPeau->GetPointTexture(i).xt;
                *(Triangle.texture+2*i+1)=itTriPeau->GetPointTexture(i).yt;
          }
        }
        indice++;
  }
        
}

void MOGL_Peau::TnL(MOGL_Afficheur * p_Afficheur)
{
  // On commence par s'occuper des triangles s'ils ont été modifiés
  if (_ModifieTriangle)
  {
                
        // On efface les tableaux utilisés pour les représenter
        MOGL_ItListe_TableauxTriangle it;
        for (it=_TabTriangle.begin();it!=_TabTriangle.end();it++)
        {
          // et on désabonne
          p_Afficheur->DesabonnerTriangle(it->Texture, it->IdentifiantAbonnement);
          free(it->TabNormale);
          free(it->TabTexture);
          free(it->TabPoint);
        }
        _TabTriangle.clear();

        // On reinitialise le tableau d'affichage des triangles
        _TabAffichageTriangle.clear();
        _TabAffichageTriangle.resize(_ListeTrianglePeau.size());


        // On prépare les structures de stockage intermédiaire des triangles
        // par texture (map de liste)
                
        typedef map <MOGL_Texture *, list<unsigned int> > MOGL_Map_TextureFace;
        typedef MOGL_Map_TextureFace::iterator MOGL_ItMap_TextureFace;
        MOGL_Map_TextureFace LaMap;
        MOGL_ItMap_TextureFace LeItMap;


        // On place chaque triangle dans la liste correspondant à sa texture
        MOGL_ItListe_TrianglePeau itTriPeau;
        unsigned int indiceFace=0;
        for (itTriPeau=_ListeTrianglePeau.begin();itTriPeau!=_ListeTrianglePeau.end();itTriPeau++)
        {
          LaMap[itTriPeau->GetTexture()].push_back(indiceFace);
          indiceFace++;
        }

        // Pour chaque texture
        for (LeItMap=LaMap.begin();LeItMap!=LaMap.end();LeItMap++)
        {
          int NbPoints=LeItMap->second.size()*3;
          int NbPointsBoucle;
          bool fin=false;
          list<unsigned int>::iterator ItListe=LeItMap->second.begin();
          while (!fin)
          {
                if (NbPoints<MOGL_Constante::NB_TRIANGLE_TABLEAU_MAX*3)
                {
                  // S'il reste moins de points à traiter que le nombre max permis
                  // par tableau, on dit que c'est la dernière fois qu'on boucle
                  fin=true;
                  NbPointsBoucle=NbPoints;
                }
                else
                {
                  // Il reste plus de points à traiter que le nombre max permis par 
                  // tableau
                  NbPointsBoucle=MOGL_Constante::NB_TRIANGLE_TABLEAU_MAX*3;
                  NbPoints-=MOGL_Constante::NB_TRIANGLE_TABLEAU_MAX*3;
                }

                // On créé une structure de tableaux pour la texture de la liste
                // courante de la taille calculée juste au dessus
                MOGL_Struct_TableauTexture UnTab;
                UnTab.Texture=LeItMap->first;

                UnTab.TabNormale=(GLfloat *)malloc(sizeof(GLfloat)*3*NbPointsBoucle);
                UnTab.TabTexture=(GLfloat *)malloc(sizeof(GLfloat)*2*NbPointsBoucle);
                UnTab.TabPoint=(GLfloat *)malloc(sizeof(GLfloat)*3*NbPointsBoucle);


                // On indique à la texture qu'il y a un une autre structure à gérer
                // p_Matrice est mis à NULL car ici, les points sont déjà transformés
                UnTab.IdentifiantAbonnement=p_Afficheur->AbonnerTriangle(LeItMap->first, &_MatriceFinale, NbPointsBoucle, &_DessinerBord,
                                                                         &_CouleurBord, &_TailleBord,
                                                                         UnTab.TabPoint, UnTab.TabNormale,UnTab.TabTexture);

                // On indique aux faces ou mettre les données des points calculés
                for(int NumFace=0;NumFace<NbPointsBoucle/3;NumFace++)
                {
                  _TabAffichageTriangle[*ItListe].normal=&(UnTab.TabNormale[9*NumFace]);
                  _TabAffichageTriangle[*ItListe].texture=&(UnTab.TabTexture[6*NumFace]);
                  _TabAffichageTriangle[*ItListe].point=&(UnTab.TabPoint[9*NumFace]);
                  ItListe++;
                }

                                
                // On memorise la structure afin de pouvoir se désabonner
                _TabTriangle.push_back(UnTab);      
          }
        }

  }

  glPushMatrix();

  // On multiplie la matrice locale
  _Matrice.MultiplierDansOpenGL();
  
  // On récupère la matrice finale
  _MatriceFinale.PrendreDeOpenGL();

  // On en déduit la matrice inverse
  _InvMatriceFinale=_MatriceFinale;
  _InvMatriceFinale.Inverser();
  
  // On effectue la transforamtion de tous les noeuds enfants
  // Il est important de la faire avant de remplir les strutures de données
  // car on a besoin des points transformés des MOGL_Armature
  MOGL_ItSet_ElementArbre it;
  for(it=_SetElement.begin();it!=_SetElement.end();it++)
  {
        (*it)->TnL(p_Afficheur);
  }

  // Les points peuvent être des références vers des MOGL_Armature.
  // On ne sait pas si leur matrice de transformation a été modifié,
  // donc on recalcule les points à caheque fois (cad qu'on reremplie
  // les tableaux utilisé par la texture à partir des références vers
  // les autres objets)
  CalculerPoints();

  _ModifieMatrice=false;
  _ModifieTriangle=false;

  glPopMatrix();


}

void MOGL_Peau::DefinirInvMatriceBase(MOGL_Armature * p_Armature)
{
  p_Armature->SetInvMatriceBase(&_InvMatriceFinale);
}

void MOGL_Peau::SetDessinerBord(bool p_Dessiner)
{
  _DessinerBord = p_Dessiner;
}

bool MOGL_Peau::GetDessinerBord() const
{
  return _DessinerBord;
}

void MOGL_Peau::SetCouleurBord(MOGL_Struct_Couleur p_Couleur)
{
  _CouleurBord = p_Couleur;
}

MOGL_Struct_Couleur MOGL_Peau::GetCouleurBord() const
{
  return _CouleurBord;
}

void MOGL_Peau::SetTailleBord(unsigned int p_TailleBord)
{
  _TailleBord = p_TailleBord;
}

unsigned int MOGL_Peau::GetTailleBord() const
{
  return _TailleBord;
}
