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

//MOGL_Objet::MOGL_Objet(): MOGL_ObjetAbstrait(), MOGL_ElementArbre()
MOGL_Objet::MOGL_Objet(): MOGL_Noeud(), _ModifieTriangle(true), _ModifieStrip(true),
//                          _AbonnementTrianglesUnique(0), _AbonnementStripsUnique(0),
                          _ProjeterOmbre(false), _Afficheur(NULL), _DessinerBord(false)
{
        
}

MOGL_Objet::MOGL_Objet(const MOGL_Objet & p_Objet): MOGL_Noeud(p_Objet), _ModifieTriangle(true), _ModifieStrip(true),
//                          _AbonnementTrianglesUnique(0), _AbonnementStripsUnique(0),
                                                    _Afficheur(NULL), _DessinerBord(p_Objet._DessinerBord)
{
  _ProjeterOmbre=p_Objet._ProjeterOmbre;

  for(MOGL_Liste_Triangle::const_iterator itTri=p_Objet._ListeTriangle.begin(); itTri!=p_Objet._ListeTriangle.end(); itTri++)
  {
    _ListeTriangle.push_back(*itTri);
  }
  for(MOGL_Liste_Strip::const_iterator itStrip=p_Objet._ListeStrip.begin(); itStrip!=p_Objet._ListeStrip.end(); itStrip++)
  {
    _ListeStrip.push_back(*itStrip);
  }
}

MOGL_Objet::~MOGL_Objet()
{
  for (MOGL_ItListe_TableauxTriangle it=_TabTriangle.begin();it!=_TabTriangle.end();it++)
  {
    // et on désabonne
    _Afficheur->DesabonnerTriangle(it->Texture,it->IdentifiantAbonnement);
    free(it->TabNormale);
    free(it->TabTexture);
    free(it->TabPoint);
  }
  _TabTriangle.clear();
  for (MOGL_ItListe_TableauxStrip it2=_TabStrip.begin();it2!=_TabStrip.end();it2++)
  {
    // et on désabonne
    _Afficheur->DesabonnerStrip(it2->Texture, it2->IdentifiantAbonnement);
    free(it2->TabNormale);
    free(it2->TabTexture);
    free(it2->TabPoint);
  }
  _TabStrip.clear();
}


void MOGL_Objet::_TnLAbonnement(MOGL_Afficheur * p_Afficheur)
{
  // On commence par s'occuper des triangles s'ils ont été modifiés
  if (_ModifieTriangle)
  {
    _ModifieTriangle=false;

    // 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
      _Afficheur->DesabonnerTriangle(it->Texture,it->IdentifiantAbonnement);
      free(it->TabNormale);
      free(it->TabTexture);
      free(it->TabPoint);
    }
    _TabTriangle.clear();


    // On prépare les structures de stockage intermédiaire des triangles
    // par texture (map de liste)
    MOGL_ItListe_Triangle it2;
    typedef map <MOGL_Texture *, list<MOGL_Struct_Point> > MOGL_Map_TexturePoint;
    typedef MOGL_Map_TexturePoint::iterator MOGL_ItMap_TexturePoint;
    MOGL_Map_TexturePoint LaMap;
    MOGL_ItMap_TexturePoint LeItMap;


    // On place chaque triangle dans la liste correspondant à sa texture
    for (it2=_ListeTriangle.begin();it2!=_ListeTriangle.end();it2++)
    {
      for(int NumPoint=0;NumPoint<3;NumPoint++)
      {
        LaMap[it2->GetTexture()].push_back(it2->GetPoint(NumPoint));
      }
    }

    // Pour chaque texture
    for (LeItMap=LaMap.begin();LeItMap!=LaMap.end();LeItMap++)
    {
      int NbPoints=LeItMap->second.size();
      int NbPointsBoucle;
      bool fin=false;
      list<MOGL_Struct_Point>::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 q'on boucle
          fin=true;
          NbPointsBoucle=NbPoints;
        }
        else
        {
          // Il reste plus de points à traitre 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 tableaus 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
        UnTab.IdentifiantAbonnement=p_Afficheur->AbonnerTriangle(LeItMap->first,&_MatriceFinale,NbPointsBoucle, &_DessinerBord,
                                                                 &_CouleurBord, &_TailleBord,
                                                                 UnTab.TabPoint,UnTab.TabNormale,UnTab.TabTexture);

        // On remplie les tableaux de la structure
        for(int NumPoint=0;NumPoint<NbPointsBoucle;NumPoint++)
        {
          UnTab.TabNormale[3*NumPoint]=ItListe->xn;
          UnTab.TabNormale[3*NumPoint+1]=ItListe->yn;
          UnTab.TabNormale[3*NumPoint+2]=ItListe->zn;

          UnTab.TabTexture[2*NumPoint]=ItListe->xt;
          UnTab.TabTexture[2*NumPoint+1]=ItListe->yt;

          UnTab.TabPoint[3*NumPoint]=ItListe->xp;
          UnTab.TabPoint[3*NumPoint+1]=ItListe->yp;
          UnTab.TabPoint[3*NumPoint+2]=ItListe->zp;
          ItListe++;
        }
        // On memorise la structure afin de pouvoir se désabonner
        _TabTriangle.push_back(UnTab);
      }
    }

  }
  // On s'occupe maintenant des strips s'ils ont été modifiés
  if (_ModifieStrip)
  {
    _ModifieStrip=false;

    // On efface les tableaux utilisés pour les représenter
    MOGL_ItListe_TableauxStrip it;
    for (it=_TabStrip.begin();it!=_TabStrip.end();it++)
    {
      // et on désabonne
      _Afficheur->DesabonnerStrip(it->Texture, it->IdentifiantAbonnement);
      free(it->TabNormale);
      free(it->TabTexture);
      free(it->TabPoint);
    }
    _TabStrip.clear();
                
                
    // Pour chaque strip
    MOGL_ItListe_Strip it2;
    for (it2=_ListeStrip.begin();it2!=_ListeStrip.end();it2++)
    {
                        
      // On crée les structures de la taille du strip
      MOGL_Struct_TableauTexture UnTab;
      int NbPoint=it2->NbPoint();
      UnTab.Texture=it2->GetTexture();

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

      // On abonne la structure au niveau de la texture
      UnTab.IdentifiantAbonnement=p_Afficheur->AbonnerStrip(it2->GetTexture(), &_MatriceFinale,NbPoint, &_DessinerBord, 
                                                            &_CouleurBord, &_TailleBord,
                                                            UnTab.TabPoint, UnTab.TabNormale, UnTab.TabTexture);

      // On stocke le premier point
      const MOGL_Struct_Point & Point=it2->GetFirstPoint();

      UnTab.TabNormale[0]=Point.xn;
      UnTab.TabNormale[1]=Point.yn;
      UnTab.TabNormale[2]=Point.zn;

      UnTab.TabTexture[0]=Point.xt;
      UnTab.TabTexture[1]=Point.yt;

      UnTab.TabPoint[0]=Point.xp;
      UnTab.TabPoint[1]=Point.yp;
      UnTab.TabPoint[2]=Point.zp;
                        
      // Et on stocke maintenant les points suivants
      for(int NumPoint=1;NumPoint<NbPoint;NumPoint++)
      {
        const MOGL_Struct_Point & Point=it2->GetNextPoint();
        UnTab.TabNormale[3*NumPoint]=Point.xn;
        UnTab.TabNormale[3*NumPoint+1]=Point.yn;
        UnTab.TabNormale[3*NumPoint+2]=Point.zn;

        UnTab.TabTexture[2*NumPoint]=Point.xt;
        UnTab.TabTexture[2*NumPoint+1]=Point.yt;

        UnTab.TabPoint[3*NumPoint]=Point.xp;
        UnTab.TabPoint[3*NumPoint+1]=Point.yp;
        UnTab.TabPoint[3*NumPoint+2]=Point.zp;
      }
      // Et on mémorise pour finir la structure du strip
      _TabStrip.push_back(UnTab);
    }
  }

}



void MOGL_Objet::TnL(MOGL_Afficheur * p_Afficheur)
{
  if (p_Afficheur!=_Afficheur)
  {
    _ModifieTriangle=true;
    _ModifieStrip=true;
  }

  _TnLAbonnement(p_Afficheur);
        

  glPushMatrix();
  _Matrice.MultiplierDansOpenGL();
  _MatriceFinale.PrendreDeOpenGL();
  MOGL_ItSet_ElementArbre it;
  for(it=_SetElement.begin();it!=_SetElement.end();it++)
  {
    (*it)->TnL(p_Afficheur);
  }

  glPopMatrix();


  // On reinitialise l'indicateur de modification de matrice,
  // même si on l'utilise pas dans cette classe
  _ModifieMatrice=false;

  if (_ProjeterOmbre)
  {
    _TnLOmbre(p_Afficheur);
  }

  _Afficheur=p_Afficheur;
  
}

void MOGL_Objet::AjouterTriangle(const MOGL_Struct_Point & p_Point1,
                                                                 const MOGL_Struct_Point & p_Point2,
                                                                 const MOGL_Struct_Point & p_Point3,
                                                                 MOGL_Texture * p_Texture)
{
  _ListeTriangle.push_back(MOGL_Triangle(p_Point1, p_Point2, p_Point3, p_Texture));
  _ModifieTriangle=true;
}

void MOGL_Objet::AjouterTriangle(const MOGL_Triangle & p_Triangle)
{
  _ListeTriangle.push_back(p_Triangle);
  _ModifieTriangle=true;
}

void MOGL_Objet::AjouterStrip(const MOGL_Strip & p_Strip)
{
  _ListeStrip.push_back(p_Strip);
  _ModifieStrip=true;
}

void MOGL_Objet::_TnLOmbre(MOGL_Afficheur * p_Afficheur)
{
  MOGL_Struct_Vecteur vec[3];
  for (MOGL_ItListe_Triangle it=_ListeTriangle.begin();it!=_ListeTriangle.end();it++)
  {
    for(int i=0; i<3; i++)
    {
      vec[i].x=it->GetPoint(i).xp;
      vec[i].y=it->GetPoint(i).yp;
      vec[i].z=it->GetPoint(i).zp;
      vec[i]=_MatriceFinale.MultiplierVecteur(vec[i]);
    }
    MOGL_TriangleOmbre tr(vec[0], vec[1], vec[2]);
    p_Afficheur->AjouterTriangleOmbre(tr);
  }
}

void MOGL_Objet::ProjeterOmbre(bool p_Val)
{
  _ProjeterOmbre=p_Val;
}

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

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

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

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

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

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