// Gmsh - Copyright (C) 1997-2013 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
//
// Contributed by Jonathan Lambrechts

#include "drawContextFltkStringTexture.h"

class drawContextFltkStringTexture::queueString {
  public :
  typedef struct {
    std::string text;
    GLfloat x, y, z;
    GLfloat r, g, b, alpha;
    int fontSize, fontId;
    int width;
    int height; 
  } element;

  private:
  std::vector<element> _elements;
  int _totalWidth, _maxHeight;

  public:
  queueString()
  {
    _totalWidth = 0;
    _maxHeight = 0;
  }

  void append(const element &elem)
  {
    if (_totalWidth + elem.width > 1000)
      flush();
    _elements.push_back(elem);
    _totalWidth += elem.width;
    _maxHeight = std::max(_maxHeight, elem.height);
  }

  void flush()
  {
    //1000 should be _totalWidth but it does not work
    int w = 1000, h = _maxHeight;
    Fl_Offscreen offscreen = fl_create_offscreen(w, h);
    fl_begin_offscreen(offscreen);
    fl_color(0, 0, 0);
    fl_rectf(0, 0, w, h);
    fl_color(255, 255, 255);
    int pos = 0;
    for(std::vector<element>::iterator it = _elements.begin(); it != _elements.end();  ++it) {
      fl_font(it->fontId, it->fontSize);
      fl_draw(it->text.c_str(), pos, it->height - fl_descent());
      pos += it->width;
    }
    uchar *data = fl_read_image(NULL, 0, 0, w, h);
    for (int i = 0; i < w * h; ++i) {
      data[i] = data[i * 3];
    }
    fl_end_offscreen();
    fl_delete_offscreen(offscreen);

    //setup matrices
    GLint matrixMode;
    GLuint textureId;
    glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
    glMatrixMode (GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity ();
    glMatrixMode (GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity ();
    float winw = Fl_Window::current()->w();
    float winh = Fl_Window::current()->h();
    glScalef (2.0f / winw, 2.0f /  winh, 1.0f);
    glTranslatef (-winw / 2.0f, -winh / 2.0f, 0.0f);
    //write the texture on screen
    glEnable (GL_TEXTURE_RECTANGLE);
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT);
    glDisable(GL_LIGHTING);
    glDisable (GL_DEPTH_TEST);
    glEnable (GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glGenTextures (1, &textureId);
    glBindTexture (GL_TEXTURE_RECTANGLE, textureId);
    glTexImage2D (GL_TEXTURE_RECTANGLE, 0, GL_ALPHA, w, h, 0,
        GL_ALPHA, GL_UNSIGNED_BYTE, data);
    //glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_SRC0_ALPHA);
    //printf("error %i %s\n", __LINE__, gluErrorString(glGetError()));
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    pos = 0;
    for(std::vector<element>::iterator it = _elements.begin(); it != _elements.end();  ++it) {
      glTranslatef(it->x, it->y, it->z);
      glColor4f(it->r, it->g, it->b, it->alpha);
      int Lx = it->width;
      int Ly = it->height;

      glBegin (GL_QUADS);
      glTexCoord2f(pos, 0);
      glVertex2f(0.0f, Ly);
      glTexCoord2f(pos + Lx, 0);
      glVertex2f(Lx, Ly);
      glTexCoord2f(pos + Lx, Ly);
      glVertex2f(Lx, 0.0f);
      glTexCoord2f(pos, Ly);
      glVertex2f(0.0f, 0.0f);
      glEnd ();
      pos += Lx;
      glTranslatef(-it->x, -it->y, -it->z);
    }
    glDeleteTextures(1, &textureId);

    glPopAttrib();

    // reset original matrices
    glPopMatrix(); // GL_MODELVIEW
    glMatrixMode (GL_PROJECTION);
    glPopMatrix();
    glMatrixMode (matrixMode);
    _elements.clear();
    _maxHeight = 0;
    _totalWidth = 0;
    delete []data;
  }
};

void drawContextFltkStringTexture::flushString()
{
  _queue->flush();
}

//ensure the surface is large enough
void drawContextFltkStringTexture::drawString(const char *str)
{
  GLfloat pos[4], color[4];
  glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
  glGetFloatv(GL_CURRENT_COLOR, color);
  queueString::element elem = {str, pos[0], pos[1], pos[2], color[0], color[1], color[2], color[3],
    _currentFontSize, _currentFontId, getStringWidth(str) + 1, getStringHeight()};
  _queue->append(elem);
}

drawContextFltkStringTexture::~drawContextFltkStringTexture()
{
  delete _queue;
}

drawContextFltkStringTexture::drawContextFltkStringTexture()
{
  _queue = new queueString;
}

void drawContextFltkStringTexture::setFont(int fontid, int fontsize)
{
  drawContextFltk::setFont(fontid, fontsize);
  _currentFontId = fontid;
  _currentFontSize = fontsize;
}