#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "internal.h"

static GLuint glFont;

static void dsDrawTextRaw(const float x, const float y, const float *color, const float scale, const char *text)
{
  static float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  const char *ptr = text;

  if (!color)
    color = white;
  glPushAttrib(GL_ALL_ATTRIB_BITS);
  glColor4f(color[0], color[1], color[2], color[3]); 
  glDisable(GL_LIGHTING);
  glEnable(GL_TEXTURE_2D);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glPushMatrix();
  glTranslatef(x, y, 0.0f);
  glScalef(scale, scale, scale);
  while (*ptr) {
    glCallList(glFont+*ptr);
    ptr++;
  }
  glPopMatrix();
  glPopAttrib();
}

void dsDrawTextMappedOnObject(const float pos[3], const float *color, const float scale, const char *text)
{
  float width, height;
  //float theta;
  //float steps = 10.0f;
  float xyz[3];
  float unit[3];
  float hpr[3];

  glPushAttrib(GL_ALL_ATTRIB_BITS);
  glDisable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glPushMatrix();
  glTranslatef(pos[0], pos[1], pos[2]);
  dsGetViewpoint(xyz, hpr);
  glDepthMask(GL_FALSE);
  xyz[0] -= pos[0];
  xyz[1] -= pos[1];
  xyz[2] -= pos[2];
  unit[0] = xyz[0];
  unit[1] = xyz[1];
  unit[2] = xyz[2];
  unit[0] /= sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
  unit[1] /= sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
  unit[2] /= sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
  glTranslatef(unit[0]*scale*4.0f, unit[1]*scale*4.0f , unit[2]*scale*4.0f);
  glRotatef(-hpr[0], 0,0,-1);
  glRotatef(hpr[1], 0,-1,0);
  glRotatef(hpr[2], -1,0,0);
  glRotatef(90, 0,-1,0);
  glRotatef(90, 0,0,-1);
  glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
  glTranslatef(-scale*1.0f, -scale*1.0f, 0.0f);
  glScalef(0.05f, 0.05f, 0.05f);
  width = 400.0f;
  height = 80.0f;
  dsDrawTextRaw(0.0f, 0.0f, color, scale, text);
  glPopMatrix();
  glPopAttrib();   
}

void dsDrawTextOnScreen (const float x, const float y, const float *color, const float scale, const char *text)
{
  if (!dsGetHud())
    return;
  glPushAttrib(GL_ALL_ATTRIB_BITS);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glFrustum(-0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1000.0f);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(-1.0f, -1.0f, -1.0f);
  glScalef(2.0f, 2.0f, 1.0f);
  glTranslatef(x, y, 0.0f);
  glScalef(1.0f/width, 1.0f/height, 1.0f);
  glDepthFunc(GL_ALWAYS);
  dsDrawTextRaw(0.0f, 0.0f, color, scale, text);
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopAttrib();
}

void dsDrawTextOnObject (const float pos[3], const float *color, const float scale, const char *text)
{
  float width, height;
  float theta;
  float steps = 10.0f;
  float hpr[3];

  if (!dsGetSpeech())
    return;
  glPushAttrib(GL_ALL_ATTRIB_BITS);
  glDisable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glDepthFunc(GL_ALWAYS);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glPushMatrix();
  glTranslatef(pos[0], pos[1], pos[2]);
  dsGetViewpoint(NULL, hpr);
  glRotatef(-hpr[0], 0,0,-1);
  glRotatef(hpr[1], 0,-1,0);
  glRotatef(hpr[2], -1,0,0);
  glRotatef(90, 0,-1,0);
  glRotatef(90, 0,0,-1);
  glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
  glScalef(0.05f, 0.05f, 0.05f);
  width = 400.0f;
  height = 80.0f;
  glBegin(GL_TRIANGLES);
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(40.0f, 40.0f, 0.0f);
  glVertex3f(20.0f, 40.0f, 0.0f);
  glVertex3f(0.0f, 40.0f, 0.0f);
  glVertex3f(width, 40.0f, 0.0f);
  glVertex3f(0.0f, 40.0f+height, 0.0f);
  glVertex3f(width, 40.0f, 0.0f);
  glVertex3f(width, 40.0f+height, 0.0f);
  glVertex3f(0.0f, 40.0f+height, 0.0f);
  for (theta = 0.0f; theta < 1.0f-1.0f/steps/2.0f; theta += 1.0f/steps) {
    glVertex3f(0.0f-sin(theta*M_PI)*height/2.0f, (cos(theta*M_PI)-1.0f)*(-height/2.0f)+40.0f, 0.0f);
    glVertex3f(0.0f, height/2.0f+40.0f, 0.0f);
    glVertex3f(0.0f-sin((theta+1.0f/steps)*M_PI)*height/2.0f, (cos((theta+1.0f/steps)*M_PI)-1.0f)*(-height/2.0f)+40.0f, 0.0f);
  }
  for (theta = 0.0f; theta < 1.0f-1.0f/steps/2.0f; theta += 1.0f/steps) {
    glVertex3f(width+sin((theta+1.0f/steps)*M_PI)*height/2.0f, (cos((theta+1.0f/steps)*M_PI)-1.0f)*(-height/2.0f)+40.0f, 0.0f);
    glVertex3f(width, height/2.0f+40.0f, 0.0f);
    glVertex3f(width+sin(theta*M_PI)*height/2.0f, (cos(theta*M_PI)-1.0f)*(-height/2.0f)+40.0f, 0.0f);
  }
  glEnd();
  glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
  glBegin(GL_LINE_STRIP);
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(20.0f, 40.0f, 0.0f);
  glVertex3f(0.0f, 40.0f, 0.0f);
  for (theta = 0.0f; theta < 1.0f+1.0f/steps; theta += 1.0f/steps) {
    glVertex3f(0.0f-sin(theta*M_PI)*height/2.0f, (cos(theta*M_PI)-1.0f)*(-height/2.0f)+40.0f, 0.0f);
  }
  for (theta = 0.0f; theta < 1.0f+1.0f/steps; theta += 1.0f/steps) {
    glVertex3f(width+sin(theta*M_PI)*height/2.0f, (cos(theta*M_PI)+1.0f)*(height/2.0f)+40.0f, 0.0f);
  }
  glVertex3f(40.0f, 40.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);
  glEnd();
  glTranslatef(5.0f, 35.0f+height/2.0f, 0.0f);
  dsDrawTextRaw(0.0f, 0.0f, color, scale, text);
  glPopMatrix();
  glPopAttrib();   
}

static void dsCreateLetter (FT_Face &fc, char c)
{
  FT_BitmapGlyph bmg;
  FT_Glyph gf;
  GLuint texture;
  
  // Load the letter's glyph & bitmap
  if (FT_Load_Glyph(fc, FT_Get_Char_Index(fc, c), FT_LOAD_DEFAULT))
    dsError("Failed to load the character 'a'");
  if (FT_Get_Glyph(fc->glyph, &gf))
    dsError("Failed to load the glyph");
  FT_Glyph_To_Bitmap(&gf, ft_render_mode_normal, 0, 1);
  bmg = (FT_BitmapGlyph)gf;
  
  // Generate a new texture for this letter
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
  //glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bmg->bitmap.width, bmg->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmg->bitmap.buffer);
  gluBuild2DMipmaps (GL_TEXTURE_2D, GL_ALPHA, bmg->bitmap.width, bmg->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, bmg->bitmap.buffer); 

  // Create a display list that is character indexed 
  glNewList(glFont+c, GL_COMPILE);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTranslatef(bmg->left, 0.0f, 0.0f);
  glPushMatrix();
  glTranslatef(0.0f, bmg->top-bmg->bitmap.rows, 0.0f);
  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f);
  glVertex3f(0.0f, bmg->bitmap.rows, 0.0f);
  glTexCoord2f(0.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);
  glTexCoord2f(1.0f, 1.0f);
  glVertex3f(bmg->bitmap.width, 0.0f, 0.0f);
  glTexCoord2f(1.0f, 0.0f);
  glVertex3f(bmg->bitmap.width, bmg->bitmap.rows, 0.0f);
  glEnd();
  glPopMatrix();
  glTranslatef(fc->glyph->advance.x / 64, 0.0f, 0.0f);
  glEndList();
}

void dsCreateFont ()
{
  unsigned char c;
  FT_Library lib;
  FT_Face fc;
 
  // Initialize FreeType 
  if (FT_Init_FreeType(&lib))
    dsError("Failed to init FreeType");
  if (FT_New_Face(lib, "./DATA/font.ttf", 0, &fc))
    dsError("Failed to load the test font");
  // 32x32 pixels font size
  FT_Set_Char_Size(fc, 32*64, 32*64, 96, 96);
  glFont = glGenLists(128);
 
  // Create a display list for each letter 
  for (c = 0; c < 128; c++) {
    dsCreateLetter(fc, c);
  };
  
  FT_Done_Face(fc);
  FT_Done_FreeType(lib);
}


