/**
* Project: VSXu: Realtime visual programming language, music/audio visualizer, animation tool and much much more.
*
* @see The Lesser GNU Public License (GPL)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser GNU General Public License
* for more details.
*
* You should have received a copy of the Lesser GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <string.h> // For memset

#include "FTGLTextureFont.h"
#include "FTTextureGlyph.h"


inline GLuint NextPowerOf2( GLuint in)
{
     in -= 1;

     in |= in >> 16;
     in |= in >> 8;
     in |= in >> 4;
     in |= in >> 2;
     in |= in >> 1;

     return in + 1;
}


FTGLTextureFont::FTGLTextureFont( const char* fontname)
:   FTFont( fontname),
    maxTextSize(0),
    textureWidth(0),
    textureHeight(0),
    glyphHeight(0),
    glyphWidth(0),
    padding(3),
    xOffset(0),
    yOffset(0)
{
    remGlyphs = numGlyphs = face.GlyphCount();
}


FTGLTextureFont::FTGLTextureFont( const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
:   FTFont( pBufferBytes, bufferSizeInBytes),
    maxTextSize(0),
    textureWidth(0),
    textureHeight(0),
    glyphHeight(0),
    glyphWidth(0),
    padding(3),
    xOffset(0),
    yOffset(0)
{
    remGlyphs = numGlyphs = face.GlyphCount();
}


FTGLTextureFont::~FTGLTextureFont()
{
    glDeleteTextures( textureIDList.size(), (const GLuint*)&textureIDList[0]);
}


FTGlyph* FTGLTextureFont::MakeGlyph( unsigned int glyphIndex)
{
    FT_GlyphSlot ftGlyph = face.Glyph( glyphIndex, FT_LOAD_NO_HINTING);
    
    if( ftGlyph)
    {
        glyphHeight = static_cast<int>( charSize.Height());
        glyphWidth = static_cast<int>( charSize.Width());
        
        if( textureIDList.empty())
        {
            textureIDList.push_back( CreateTexture());
            xOffset = yOffset = padding;
        }
        
        if( xOffset > ( textureWidth - glyphWidth))
        {
            xOffset = padding;
            yOffset += glyphHeight;
            
            if( yOffset > ( textureHeight - glyphHeight))
            {
                textureIDList.push_back( CreateTexture());
                yOffset = padding;
            }
        }
        
        FTTextureGlyph* tempGlyph = new FTTextureGlyph( ftGlyph, textureIDList[textureIDList.size() - 1],
                                                        xOffset, yOffset, textureWidth, textureHeight);
        xOffset += static_cast<int>( tempGlyph->BBox().upperX - tempGlyph->BBox().lowerX + padding);
        
        --remGlyphs;
        return tempGlyph;
    }
    
    err = face.Error();
    return NULL;
}


void FTGLTextureFont::CalculateTextureSize()
{
    if( !maxTextSize)
    {
        glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextSize);
    }
    
    textureWidth = NextPowerOf2( (remGlyphs * glyphWidth) + ( padding * 2));
    if( textureWidth > maxTextSize)
    {
        textureWidth = maxTextSize;
    }
    
    int h = static_cast<int>( (textureWidth - ( padding * 2)) / glyphWidth);
        
    textureHeight = NextPowerOf2( (( numGlyphs / h) + 1) * glyphHeight);
    textureHeight = textureHeight > maxTextSize ? maxTextSize : textureHeight;
}


GLuint FTGLTextureFont::CreateTexture()
{   
    CalculateTextureSize();
    
    int totalMemory = textureWidth * textureHeight;
    unsigned char* textureMemory = new unsigned char[totalMemory];
    memset( textureMemory, 0, totalMemory);

    GLuint textID;
    glGenTextures( 1, (GLuint*)&textID);

    glBindTexture( GL_TEXTURE_2D, textID);
    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_MAG_FILTER, GL_LINEAR);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //gluBuild2DMipmaps(GL_TEXTURE_2D,1,textureWidth,textureHeight,GL_R,GL_UNSIGNED_BYTE,textureMemory);
    glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, textureMemory);

    delete [] textureMemory;

    return textID;
}


bool FTGLTextureFont::FaceSize( const unsigned int size, const unsigned int res)
{
    if( !textureIDList.empty())
    {
        glDeleteTextures( textureIDList.size(), (const GLuint*)&textureIDList[0]);
        remGlyphs = numGlyphs = face.GlyphCount();
    }

    return FTFont::FaceSize( size, res);
}


void FTGLTextureFont::Render( const char* string)
{   
    //glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
    
    //glEnable(GL_BLEND);
    //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
    
    FTFont::Render( string);

    //glPopAttrib();
}


void FTGLTextureFont::Render( const wchar_t* string)
{   
    //glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
    
    //glEnable(GL_BLEND);
    //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
    
    FTFont::Render( string);
    
    //glPopAttrib();
}

