/*****************************************************************************
 *
 *  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_PoliceTTF.h"
#include <fstream>
#include <GL/glext.h>

#if 1
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define    rmask 0xff000000
#define    gmask 0x00ff0000
#define    bmask 0x0000ff00
#define    amask 0x000000ff
#else
#define    rmask 0x000000ff
#define    gmask 0x0000ff00
#define    bmask 0x00ff0000
#define    amask 0xff000000
#endif
#else
#define    rmask 0x00ff0000
#define    gmask 0x0000ff00
#define    bmask 0x000000ff
#define    amask 0xff000000
#endif


#define TEXTURE_SIZE   256

MOGL_PoliceTTF::MOGL_PoliceTTF(unsigned int p_Size, int p_Style ): _NbCaractere(0), _Caracteres(NULL),_LastTextureX(0),
                                                                   _LastTextureY(0), maxHeightCarac(0), _Font(NULL), _Size(p_Size), _Style(p_Style)
{
    // Check OpenGL version
    string stVersion(reinterpret_cast<const char *>(glGetString(GL_VERSION)));
    string stDeb = stVersion.substr(0,3);
    if ((stDeb == "1.0") || (stDeb == "1.1"))
    {
        // Old version !
        cerr<<"OpenGL version "<<stVersion<<" -> You should use version >= 1.2" <<endl;
        _bOldOpenGl = true;
    }
    else
    {
        _bOldOpenGl = false;
    }

    if (!TTF_WasInit())
    {
        if (TTF_Init() == -1)
        {
            cerr<<"TTF_Init error: "<<TTF_GetError()<<endl;
        }
    }
}

bool MOGL_PoliceTTF::Charger(const char * p_NomFichier)
{

  _Font = TTF_OpenFontIndex( p_NomFichier, _Size, 0);
  if (_Font == NULL)
  {
    cerr<<"TTF_OpenFont error: "<<TTF_GetError()<<endl;
    return false;
  }
  else
  {
    TTF_SetFontStyle(_Font, _Style);
    _NomFichier=p_NomFichier;

    return true;
  }
}

MOGL_PoliceTTF::~MOGL_PoliceTTF()
{
  if (_Font != NULL)
  {
    TTF_CloseFont(_Font);
  }

  for(MOGL_PoliceTTF::MOGL_Vector_TextureName::iterator it = _VectorTextureName.begin(); it !=  _VectorTextureName.end(); it++)
  {
    SDL_FreeSurface(it->SurfaceSDL);
  }
}

void MOGL_PoliceTTF::Afficher(const string & p_Chaine, int p_X, int p_Y, const MOGL_Struct_Couleur & p_CouleurTexte) const
{
  
  MOGL_PoliceTTF & self = *(const_cast<MOGL_PoliceTTF *>(this));
  glEnable(GL_TEXTURE_2D);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  glDisable(GL_ALPHA_TEST);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);   

  glColor4f(p_CouleurTexte.r, p_CouleurTexte.g, p_CouleurTexte.b, p_CouleurTexte.a);

  float x=p_X;
  float y=p_Y;


  unsigned int taille;
  Uint16 * unicStr = _AllocAndConvertToUCS2(p_Chaine, taille);
  for(unsigned int i=0; i<taille; i++)
  {
    MOGL_Struct_Carac * carac = self._GetCarac(unicStr[i]);
    glBindTexture(GL_TEXTURE_2D, carac->TextureName);
    glBegin(GL_QUADS);
    x=self._AfficherCaractere(x, y, carac);
    glEnd();
  }
  delete [] unicStr;
}


void MOGL_PoliceTTF::Afficher(const string & p_Chaine, int p_X, int p_Y, 
                           const MOGL_Struct_Couleur & p_CouleurTexte, const MOGL_Struct_Couleur & p_CouleurFond) const
{
  MOGL_PoliceTTF & self = *(const_cast<MOGL_PoliceTTF *>(this));

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  glDisable(GL_ALPHA_TEST);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);   


  float x=p_X;
  float y=p_Y;
  unsigned int taille;
  Uint16 * unicStr = _AllocAndConvertToUCS2(p_Chaine, taille);
  for(unsigned int i=0; i<taille; i++)
  {
    glDisable(GL_TEXTURE_2D);
    glColor4f(p_CouleurFond.r, p_CouleurFond.g, p_CouleurFond.b, p_CouleurFond.a);
    MOGL_Struct_Carac * carac = self._GetCarac(unicStr[i]);
    glBegin(GL_QUADS);
    self._AfficherFondCaractere(x, y, carac);
    glEnd();
    glBindTexture(GL_TEXTURE_2D, carac->TextureName);

    glEnable(GL_TEXTURE_2D);
    glColor4f(p_CouleurTexte.r, p_CouleurTexte.g, p_CouleurTexte.b, p_CouleurTexte.a);
    glBegin(GL_QUADS);
    x=self._AfficherCaractere(x, y, carac);
    glEnd();
  }
  delete [] unicStr;
}

float MOGL_PoliceTTF::_AfficherCaractere(float p_X, float p_Y, MOGL_Struct_Carac * p_Caractere)
{
  if (p_Caractere)
  {
    unsigned int largeur=p_Caractere->x2 - p_Caractere->x1;
    unsigned int hauteur=p_Caractere->y2 - p_Caractere->y1;

    glTexCoord2f(p_Caractere->x1/static_cast<float>(TEXTURE_SIZE), (p_Caractere->y1)/static_cast<float>(TEXTURE_SIZE));
    glVertex2f(p_X, p_Y);

    glTexCoord2f(p_Caractere->x1/static_cast<float>(TEXTURE_SIZE), (p_Caractere->y2)/static_cast<float>(TEXTURE_SIZE));
    glVertex2f(p_X, p_Y+hauteur);

    glTexCoord2f((p_Caractere->x2)/static_cast<float>(TEXTURE_SIZE), (p_Caractere->y2)/static_cast<float>(TEXTURE_SIZE));
    glVertex2f(p_X+largeur, p_Y+hauteur);

    glTexCoord2f((p_Caractere->x2)/static_cast<float>(TEXTURE_SIZE), (p_Caractere->y1)/static_cast<float>(TEXTURE_SIZE));
    glVertex2f(p_X+largeur, p_Y);

    return p_X+largeur;
  }
  else
  {
    return p_X;
  }
}

float MOGL_PoliceTTF::_AfficherFondCaractere(float p_X, float p_Y, MOGL_Struct_Carac * p_Caractere)
{
  if (p_Caractere)
  {
    unsigned int largeur=p_Caractere->x2-p_Caractere->x1;
    unsigned int hauteur=p_Caractere->y2-p_Caractere->y1;
  
    glVertex2f(p_X, p_Y);
    glVertex2f(p_X, p_Y+hauteur);
    glVertex2f(p_X+largeur, p_Y+hauteur);
    glVertex2f(p_X+largeur, p_Y);

    return p_X+largeur;
  }
  else
  {
    return p_X;
  }
}


unsigned int MOGL_PoliceTTF::GetTailleXCaractere(Uint16 p_Caractere) const
{
  MOGL_Map_Carac::const_iterator it = _MapCarac.find(p_Caractere);
  if (it != _MapCarac.end())
  {
    return it->second.x2 - it->second.x1;
  }
  else
  {
    return 0;
  }
}

unsigned int MOGL_PoliceTTF::GetTailleYCaractere(Uint16 p_Caractere) const
{
  MOGL_Map_Carac::const_iterator it = _MapCarac.find(p_Caractere);
  if (it != _MapCarac.end())
  {
    return it->second.y2 - it->second.y1;
  }
  else
  {
    return 0;
  }
}

bool MOGL_PoliceTTF::Recharger()
{
    MOGL_Struct_TextureCarac texture;

    for(MOGL_Vector_TextureName::iterator it=_VectorTextureName.begin(); it != _VectorTextureName.end(); it++)
    {
        texture = *it;
        glBindTexture(GL_TEXTURE_2D,texture.TextureName);
        if (_bOldOpenGl)
        {
            glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TEXTURE_SIZE,TEXTURE_SIZE,0,GL_RGBA,GL_UNSIGNED_BYTE,texture.SurfaceSDL->pixels);
        }
        else
        {
            glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TEXTURE_SIZE,TEXTURE_SIZE,0,GL_RGBA,GL_UNSIGNED_INT_8_8_8_8,texture.SurfaceSDL->pixels);
        }
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    }
    return true;
}

MOGL_PoliceTTF::MOGL_Struct_Carac * MOGL_PoliceTTF::_GetCarac(Uint16 p_Carac)
{
  MOGL_Map_Carac::iterator it = _MapCarac.find(p_Carac);
  if (it == _MapCarac.end())
  {
    if (_CreateCarac(p_Carac))
    {
      return &_MapCarac[p_Carac];
    }
    else
    {
      return NULL;
    }
  }
  else
  {
    return &(it->second);
  }

}

bool MOGL_PoliceTTF::_CreateCarac(Uint16 p_Carac)
{
  MOGL_Struct_TextureCarac texture;

  // On fabrique le caractère avec SDL_ttf
  SDL_Color sdlColorFg = {255,255,255, 255};
  SDL_Color sdlColorBg = {0,255,255,255};
  Uint16 c[2];
  c[0] = p_Carac;
  c[1] = 0;
  SDL_Surface *text_surface = TTF_RenderUNICODE_Shaded(_Font, c, sdlColorFg, sdlColorBg);
  if (text_surface == NULL)
  {
    cerr<<"TTF_RenderText_Solid error: "<<TTF_GetError()<<endl;
  }

  // On regarde si le caractère passe dans la dernière texture
  if (_LastTextureX + text_surface->clip_rect.w  >= TEXTURE_SIZE)
  {
    _LastTextureX = 0;
    _LastTextureY += maxHeightCarac;
  }
  if (_LastTextureY + text_surface->clip_rect.h >= TEXTURE_SIZE)
  {
    // Plus de place, on commence une nouvelle texture
    _LastTextureY = 0;
    _LastTextureX = 0;
    maxHeightCarac = 0;
    texture = _CreateSurface();
    _VectorTextureName.push_back(texture);
  }
  else
  {
    if (_VectorTextureName.size() == 0)
    {
      texture = _CreateSurface();
      _VectorTextureName.push_back(texture);
    }
    else
    {
      texture = *_VectorTextureName.rbegin();
      glBindTexture(GL_TEXTURE_2D,texture.TextureName);
    }
  }

  if (maxHeightCarac < text_surface->clip_rect.h)
  {
    maxHeightCarac = text_surface->clip_rect.h;
  }
  if (_HauteurMax < maxHeightCarac)
  {
    _HauteurMax = maxHeightCarac;
  }
  SDL_Rect newRect = {_LastTextureX, _LastTextureY, text_surface->clip_rect.w, text_surface->clip_rect.h};

  if (SDL_BlitSurface(text_surface, &text_surface->clip_rect, texture.SurfaceSDL, &newRect) != 0)
  {
    cerr<<"SDL_BlitSurface error"<<endl;
  }

  MOGL_Struct_Carac newCarac = {texture.TextureName, p_Carac, _LastTextureX, _LastTextureY, 
                                _LastTextureX+text_surface->clip_rect.w, _LastTextureY+text_surface->clip_rect.h};

  if (_bOldOpenGl)
  {
      glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TEXTURE_SIZE,TEXTURE_SIZE,0,GL_RGBA,GL_UNSIGNED_BYTE,texture.SurfaceSDL->pixels);
  }
  else
  {
      glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TEXTURE_SIZE,TEXTURE_SIZE,0,GL_RGBA,GL_UNSIGNED_INT_8_8_8_8,texture.SurfaceSDL->pixels);
  }
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
  //glTexSubImage2D(GL_TEXTURE_2D,0, _LastTextureX, _LastTextureY, text_surface->clip_rect.w, text_surface->clip_rect.h, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, text_surface->pixels);

  int res = glGetError();
  if (res != 0)
  {
    cerr<<"glTexImage2D error: "<<res<<endl;
  }
  _MapCarac[p_Carac] = newCarac;

  _LastTextureX += text_surface->clip_rect.w;
  SDL_FreeSurface(text_surface);
  return true;
}

MOGL_PoliceTTF::MOGL_Struct_TextureCarac MOGL_PoliceTTF::_CreateSurface()
{
  GLuint textureName;
  glGenTextures(1,&textureName);
  glBindTexture(GL_TEXTURE_2D,textureName);
  SDL_Surface * SurfaceSDL = SDL_CreateRGBSurface(SDL_SWSURFACE, TEXTURE_SIZE, TEXTURE_SIZE, 32, rmask, gmask, bmask, amask);
  if (SurfaceSDL == NULL)
  {
    cerr<<"SDL_CreateRGBSurface error: "<<SDL_GetError()<<endl;
  }
  MOGL_Struct_TextureCarac textureCarac = {textureName, SurfaceSDL};
  return textureCarac;

}
