/*
 * util/TextureFont.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * This program 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.
 * 
 * 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
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the 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 "GFX.h"

#include "TextureFont.h"
#include "Texload.h"
#include "TextureManager.h"

#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>

TextureFont::TextureFont( const char * const name ) {
        
        _glyphsIndex   = 0;
        _numGlyphLists = 0;
        
        ok = false;
        _textureIndex = 0;

        cout << " :: TextureFont loading " << name << endl;

        if ( ! initGlyphLists( 256 ) ) {
                cout << " :: TextureFont unable to create texture glyph display lists." << endl;
                return;
        }
        
        int w, h;
        
        char filename[ 1024 ];
       
        sprintf( filename, "%s/%s.pgm", GFX::TEXTURE_FONT_DIR.c_str(), name );

        GLubyte * pixels = Texload::loadPGM( filename, &w, &h, false );
        
        if ( pixels == 0 ) {
                cout << " :: TextureFont load of " << filename << " failed." << endl;
                return;
        }

        if ( ( w % 16 ) || ( h % 16 ) ) {
                cout << " :: Dimensions of texture font " << w 
                     << "x" << h 
                     << " are not evenly divisible by 16." << endl
                     << " :: Texture fonts must have 16x16 glyphs." << endl;
                delete[] pixels;
                return;
        }

        //
        // expand pixels from luminance (grayscale) to luminance-alpha
        //

        GLubyte * la_pixels = new GLubyte[ w * h * 2 ];

        for ( int i = 0; i < w * h; ++i ) {
                la_pixels[i*2     ] = pixels[i];
                la_pixels[i*2 + 1 ] = pixels[i];
        }
        
        delete[] pixels;
        
        glGenTextures( 1, &_textureIndex );
        
        glPixelStorei( GL_UNPACK_ROW_LENGTH, w );
        
        glBindTexture( GL_TEXTURE_2D, _textureIndex );
        
        glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        
        glTexImage2D( GL_TEXTURE_2D, 
                      0,              // base image number (mipmap index)
                      2,              // color components
                      w,              // width 
                      h,              // height 
                      0,              // border
                      GL_LUMINANCE_ALPHA,       // format
                      GL_UNSIGNED_BYTE, 
                      la_pixels );
        
        delete[] la_pixels;
        
        _height = h / 16;
        
        float glyph_size[2] = { w / 16, h / 16 };

        float glyph_verts[4][2] = {
                { 0,      0 },
                { w / 16, 0 },
                { w / 16, h / 16 },
                { 0,      h / 16 }
        };
        
        for ( int i = 0; i < 256; ++i ) {

                _leads[ i ]    = 8;
                _widths[ i ] = 9;//(char) glyph_size[0] - 4;

                glNewList( _glyphsIndex + i, GL_COMPILE );
                {

                        glBegin( GL_QUADS );
                        {

                                float tex_origin[2] = {
                                        ( ( i % 16 ) * glyph_size[0] ) / w,
                                        ( ( i / 16 ) * glyph_size[1] + glyph_size[1] - 1 ) / h
                                };

                                float extents[2] = { glyph_size[0] / w,
                                                     glyph_size[1] / h };
                                
                                float tex_verts[4][2] = {
                                        { tex_origin[0],
                                          tex_origin[1] },
                                        { tex_origin[0] + extents[0],
                                          tex_origin[1] },
                                        { tex_origin[0] + extents[0],
                                          tex_origin[1] - extents[1] },
                                        { tex_origin[0],
                                          tex_origin[1] - extents[1] }
                                };

                                glTexCoord2fv(  tex_verts[0] );
                                glVertex2fv(   glyph_verts[0] );
                                glTexCoord2fv(  tex_verts[1] );
                                glVertex2fv(   glyph_verts[1] );
                                glTexCoord2fv(  tex_verts[2] );
                                glVertex2fv(   glyph_verts[2] );
                                glTexCoord2fv(  tex_verts[3] );
                                glVertex2fv(   glyph_verts[3] );
                        }
                        glEnd();
                        
                        glTranslatef( _widths[ i ], 0, 0 );

                }
                glEndList();
        }

        ok = true;
}

void TextureFont::startStringDraw() const {

        glBindTexture( GL_TEXTURE_2D, _textureIndex );

        glEnable( GL_TEXTURE_2D );
        
        glAlphaFunc( GL_GREATER, 0.0 );
        glEnable( GL_ALPHA_TEST );

        glTexParameterf( GL_TEXTURE_2D,
                         GL_TEXTURE_MIN_FILTER,
                         GL_LINEAR );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
        glEnable( GL_BLEND );  

        glListBase( _glyphsIndex );
}

void TextureFont::endStringDraw() const {
        glDisable( GL_ALPHA_TEST );
        glDisable( GL_BLEND );
        glDisable( GL_TEXTURE_2D );
}

TextureFont::~TextureFont() {
        if ( _textureIndex )
                glDeleteTextures( 1, &_textureIndex );
        if ( _glyphsIndex && _numGlyphLists )
                glDeleteLists( _glyphsIndex, _numGlyphLists );
}


bool TextureFont::initGlyphLists( int num ) {

        if ( _glyphsIndex && _numGlyphLists ) {
                glDeleteLists( _glyphsIndex, _numGlyphLists );
        }
        
        _glyphsIndex = 0;
        _numGlyphLists = 0;
        
        _glyphsIndex = glGenLists( num );

        GLenum gl_error;

        if ( ( gl_error = glGetError() ) ) {
                cout << " :: TextureFont::TextureFont() gl_error 1 " << (long) gl_error << endl;
                return false;
        }
        else if ( _glyphsIndex == 0 ) {
                cout << " :: TextureFont::TextureFont() unable to create " << num << " glyph lists." << endl;
                return false;
        }
        
        _numGlyphLists = num;
        return true;
}

unsigned int TextureFont::getStringWidth( const char * const str ) const {
        
        int total_width = 0;
        
        for ( const char *ptr = str; *ptr; ++ptr ) {
                total_width += _widths[ *ptr ];
        }
        
        return total_width;
}

/**
 * maxlen default 0
 */

void TextureFont::drawString( const char * const str, int maxlen ) const {

        startStringDraw();

        int len = strlen( str );

        if ( maxlen )
                len = min( len, maxlen );

        glCallLists( len, GL_BYTE, str );
        
        endStringDraw();
}
