/*
     PLIB - A Suite of Portable Game Libraries
     Copyright (C) 1998,2002  Steve Baker

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
     version 2 of the License, or (at your option) any later version.

     This library 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
     Library General Public License for more details.

     You should have received a copy of the GNU Library General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

     For further information visit http://plib.sourceforge.net

     $Id: fntTXF.cxx 1735 2002-12-01 18:21:48Z sjbaker $
*/


#include "fntLocal.h"

#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>

FILE *_fntCurrImageFd ;
int _fntIsSwapped = FALSE ;

static void tex_make_mip_maps ( GLubyte *image, int xsize,
                                     int ysize, int zsize )
{
  GLubyte *texels [ 20 ] ;   /* One element per level of MIPmap */

  for ( int l = 0 ; l < 20 ; l++ )
    texels [ l ] = NULL ;

  texels [ 0 ] = image ;

  int lev ;

  for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 ||
                   ( ysize >> (lev+1) ) != 0 ) ; lev++ )
  {
    /* Suffix '1' is the higher level map, suffix '2' is the lower level. */

    int l1 = lev   ;
    int l2 = lev+1 ;
    int w1 = xsize >> l1 ;
    int h1 = ysize >> l1 ;
    int w2 = xsize >> l2 ;
    int h2 = ysize >> l2 ;

    if ( w1 <= 0 ) w1 = 1 ;
    if ( h1 <= 0 ) h1 = 1 ;
    if ( w2 <= 0 ) w2 = 1 ;
    if ( h2 <= 0 ) h2 = 1 ;

    texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ;

    for ( int x2 = 0 ; x2 < w2 ; x2++ )
      for ( int y2 = 0 ; y2 < h2 ; y2++ )
        for ( int c = 0 ; c < zsize ; c++ )
        {
          int x1   = x2 + x2 ;
          int x1_1 = ( x1 + 1 ) % w1 ;
          int y1   = y2 + y2 ;
          int y1_1 = ( y1 + 1 ) % h1 ;

	  int t1 = texels [ l1 ] [ (y1   * w1 + x1  ) * zsize + c ] ;
	  int t2 = texels [ l1 ] [ (y1_1 * w1 + x1  ) * zsize + c ] ;
	  int t3 = texels [ l1 ] [ (y1   * w1 + x1_1) * zsize + c ] ;
	  int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ;

          texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] =
                                           ( t1 + t2 + t3 + t4 ) / 4 ;
        }
  }

  texels [ lev+1 ] = NULL ;

  if ( ! ((xsize & (xsize-1))==0) ||
       ! ((ysize & (ysize-1))==0) )
  {
    fntSetError ( SG_ALERT,
      "TXFloader: TXF Map is not a power-of-two in size!" ) ;
  }

  glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ;

  int map_level = 0 ;

#ifdef PROXY_TEXTURES_ARE_NOT_BROKEN
  int ww ;

  do
  {
    glTexImage2D  ( GL_PROXY_TEXTURE_2D,
                     map_level, zsize, xsize, ysize, FALSE /* Border */,
                            (zsize==1)?GL_LUMINANCE:
                            (zsize==2)?GL_LUMINANCE_ALPHA:
                            (zsize==3)?GL_RGB:
                                       GL_RGBA,
                            GL_UNSIGNED_BYTE, NULL ) ;

    glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ;

    if ( ww == 0 )
    {
      delete [] texels [ 0 ] ;
      xsize >>= 1 ;
      ysize >>= 1 ;

      for ( int l = 0 ; texels [ l ] != NULL ; l++ )
        texels [ l ] = texels [ l+1 ] ;

      if ( xsize < 64 && ysize < 64 )
      {
        fntSetError ( SG_ALERT,
          "FNT: OpenGL will not accept a font texture?!?" ) ;
      }
    }
  } while ( ww == 0 ) ;
#endif

  for ( int i = 0 ; texels [ i ] != NULL ; i++ )
  {
    int w = xsize>>i ;
    int h = ysize>>i ;

    if ( w <= 0 ) w = 1 ;
    if ( h <= 0 ) h = 1 ;

    glTexImage2D  ( GL_TEXTURE_2D,
                     map_level, zsize, w, h, FALSE /* Border */,
                            (zsize==1)?GL_LUMINANCE:
                            (zsize==2)?GL_LUMINANCE_ALPHA:
                            (zsize==3)?GL_RGB:
                                       GL_RGBA,
                            GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ;
    map_level++ ;
    delete [] texels [ i ] ;
  }
}



int fntTexFont::loadTXF ( const SGPath& path, GLenum mag, GLenum min )
{
  FILE *fd ;
  const auto ps = path.utf8Str();
#if defined(SG_WINDOWS)
    std::wstring fp = path.wstr();
    fd = _wfopen(fp.c_str(), L"rb");
#else
    fd = fopen(ps.c_str(), "rb");
#endif
  if ( fd == nullptr )
  {
    fntSetError ( SG_WARN,
      "fntLoadTXF: Failed to open '%s' for reading.", ps.c_str() ) ;
    return FNT_FALSE ;
  }

  _fntCurrImageFd = fd ;

  unsigned char magic [ 4 ] ;

  if ( (int)fread ( &magic, sizeof (unsigned int), 1, fd ) != 1 )
  {
    fntSetError ( SG_WARN,
      "fntLoadTXF: '%s' an empty file!", ps.c_str() ) ;
    return FNT_FALSE ;
  }

  if ( magic [ 0 ] != 0xFF || magic [ 1 ] != 't' ||
       magic [ 2 ] != 'x'  || magic [ 3 ] != 'f' )
  {
    fntSetError ( SG_WARN,
      "fntLoadTXF: '%s' is not a 'txf' font file.", ps.c_str() ) ;
    return FNT_FALSE ;
  }

  _fntIsSwapped  = FALSE ;
  int endianness  = _fnt_readInt () ;

  _fntIsSwapped  = ( endianness != 0x12345678 ) ;

  int format      = _fnt_readInt () ;
  int tex_width   = _fnt_readInt () ;
  int tex_height  = _fnt_readInt () ;
  int max_height  = _fnt_readInt () ;
                    _fnt_readInt () ;
  int num_glyphs  = _fnt_readInt () ;

  int w = tex_width  ;
  int h = tex_height ;

  float xstep = 0.5f / (float) w ;
  float ystep = 0.5f / (float) h ;

  int i, j ;

  /*
    Load the TXF_Glyph array
  */

  TXF_Glyph glyph ;

  for ( i = 0 ; i < num_glyphs ; i++ )
  {
    glyph . ch      = _fnt_readShort () ;

    glyph .  w      = _fnt_readByte  () ;
    glyph .  h      = _fnt_readByte  () ;
    glyph . x_off   = _fnt_readByte  () ;
    glyph . y_off   = _fnt_readByte  () ;
    glyph . step    = _fnt_readByte  () ;
    glyph . unknown = _fnt_readByte  () ;
    glyph . x       = _fnt_readShort () ;
    glyph . y       = _fnt_readShort () ;

    setGlyph ( (char) glyph.ch,
          (float)  glyph.step              / (float) max_height,
          (float)  glyph.x                 / (float) w + xstep,
          (float)( glyph.x + glyph.w )     / (float) w + xstep,
          (float)  glyph.y                 / (float) h + ystep,
          (float)( glyph.y + glyph.h )     / (float) h + ystep,
          (float)  glyph.x_off             / (float) max_height,
          (float)( glyph.x_off + glyph.w ) / (float) max_height,
          (float)  glyph.y_off             / (float) max_height,
          (float)( glyph.y_off + glyph.h ) / (float) max_height ) ;
  }

  exists [ static_cast<int>(' ') ] = FALSE ;

  /*
    Load the image part of the file
  */

  int ntexels = w * h ;

  unsigned char *teximage  ;
  unsigned char *texbitmap ;

  switch ( format )
  {
    case FNT_BYTE_FORMAT:
      {
        unsigned char *orig = new unsigned char [ ntexels ] ;

        if ( (int)fread ( orig, 1, ntexels, fd ) != ntexels )
        {
          fntSetError ( SG_WARN,
            "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ;
          return FNT_FALSE ;
        }

        teximage = new unsigned char [ 2 * ntexels ] ;

        for ( i = 0 ; i < ntexels ; i++ )
        {
          teximage [ i*2     ] = orig [ i ] ;
          teximage [ i*2 + 1 ] = orig [ i ] ;
        }

        delete [] orig ;
      }
      break ;
   
    case FNT_BITMAP_FORMAT:
      {
        int stride = (w + 7) >> 3;

        texbitmap = new unsigned char [ stride * h ] ;

        if ( (int)fread ( texbitmap, 1, stride * h, fd ) != stride * h )
        {
          delete [] texbitmap ;
          fntSetError ( SG_WARN,
            "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ;
          return FNT_FALSE ;
      	}

        teximage = new unsigned char [ 2 * ntexels ] ;

        for ( i = 0 ; i < 2 * ntexels ; i++ )
	  teximage [ i ] = 0 ;

        for (i = 0; i < h; i++)
	  for (j = 0; j < w; j++)
	    if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7)))
	    {
	      teximage[(i * w + j) * 2    ] = 255;
	      teximage[(i * w + j) * 2 + 1] = 255;
	    }

        delete [] texbitmap ;
      }
      break ;

    default:
      fntSetError ( SG_WARN,
        "fntLoadTXF: Unrecognised format type in '%s'.", ps.c_str() ) ;
      return FNT_FALSE ;
  }

  fclose ( fd ) ;

  fixed_pitch = FALSE ;

#ifdef GL_VERSION_1_1
  glGenTextures ( 1, & texture ) ;
  glBindTexture ( GL_TEXTURE_2D, texture ) ;
#else
  /* This is only useful on some ancient SGI hardware */
  glGenTexturesEXT ( 1, & texture ) ;
  glBindTextureEXT ( GL_TEXTURE_2D, texture ) ;
#endif

  tex_make_mip_maps ( teximage, w, h, 2 ) ;

  glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;

  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag ) ;
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min ) ;
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ;
#ifdef GL_VERSION_1_1
  glBindTexture ( GL_TEXTURE_2D, 0 ) ;
#else
  glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ;
#endif
  
  return FNT_TRUE ;
}