/****************************************************************************/
/*                                                                          */
/*  The FreeType project - a free and portable quality TrueType renderer.   */
/*                                                                          */
/*  Copyright 1996, 1997 by                                                 */
/*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
/*                                                                          */
/*  ftzoom : A simple glyph viewer.  Now supports graylevel rendering       */
/*           with the '-g' option.                                          */
/*                                                                          */
/*           use -p <platformID> together with -e <encodingID> to select    */
/*           a cmap;                                                        */
/*                                                                          */
/*                                                                          */
/*  Keys :                                                                  */
/*                                                                          */
/*  x :   fine counter_clockwise rotation                                   */
/*  c :   fine clockwise rotation                                           */
/*                                                                          */
/*  v :   fast counter_clockwise rotation                                   */
/*  b :   fast clockwise rotation                                           */
/*                                                                          */
/*  + :   fast scale up                                                     */
/*  - :   fast scale down                                                   */
/*  u :   fine scale down                                                   */
/*  j :   fine scale up                                                     */
/*                                                                          */
/*  l :   go to next glyph                                                  */
/*  k :   go to previous glyph                                              */
/*                                                                          */
/*  o :   go to tenth next glyph                                            */
/*  i :   go to tenth previous glyph                                        */
/*                                                                          */
/*  0 :   go to hundredth next glyph                                        */
/*  9 :   go to hundredth previous glyph                                    */
/*                                                                          */
/*  ) :   go to 1000th next glyph                                           */
/*  ( :   go to 1000th previous glyph                                       */
/*                                                                          */
/*  } :   go to 10000th next glyph                                          */
/*  { :   go to 10000th previous glyph                                      */
/*                                                                          */
/*    q :                                                                   */
/*  ESC :   exit                                                            */
/*                                                                          */
/*                                                                          */
/*  NOTE : This is just a test program that is used to show off and         */
/*         debug the current engine; which is still in alpha. In no         */
/*         way does it shows the final high-level interface that            */
/*         client applications will use. Wait for at least a beta for       */
/*         this.                                                            */
/*                                                                          */
/****************************************************************************/

#ifdef ARM
#include "std.h"
#include "graflink.h"
#endif

#include "freetype.h"
#include "tterror.h"  /* for Panic() and Message() only */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "gmain.h"
#include "gevents.h"
#include "gdriver.h"


#ifdef ARM
#include "armsup.c" /* pull in our routines */
#endif

#include "common.h"

#define  Pi         3.1415926535

#define  MAXPTSIZE  500                 /* dtp */

  char              Header[128];

  TT_Engine    engine;
  TT_Face      face;
  TT_Instance  instance;
  TT_Glyph     org_glyph;   /* original glyph */
  TT_Glyph     glyph;       /* rotated glyph  */
  TT_CharMap   char_map;
 
  TT_Glyph_Metrics     metrics;
  TT_Outline           org_outline;  /* original glyph outline */
  TT_Outline           outline;      /* rotated glyph oultine  */
  TT_Face_Properties   properties;
  TT_Instance_Metrics  imetrics;
 
  int  num_glyphs;
  int  xcenter_upem;
  int  ycenter_upem;
  int  units_per_em;

  int    ptsize;
  int    old_ptsize;
  int    rotation;
  int    old_rotation;

  TT_Matrix  matrix;
  int        apply_matrix;
  int        xcenter;
  int        ycenter;
  int        xoffset;
  int        yoffset;

  TT_Raster_Map  Bit;

  int            Fail;
  int            Num;
  int            Code;
  unsigned char  autorun;

  int            gray_render = 0;
  int            hinted = 1;
  int            use_cmap = 0;


  static void  ClearData( void );

  char          palette[5] = { 0, 1, 2, 3, 4 };

  void  Init_Raster_Area()
  {
    Bit.rows  = vio_Height;   /* The whole window */
    Bit.width = vio_Width;
    Bit.flow  = TT_Flow_Up;
 
    if ( gray_render )
    {
      Bit.cols  = Bit.width;
      Bit.size  = Bit.rows * Bit.width;
    }
    else
    {
      Bit.cols  = ( Bit.width + 7 ) / 8;    /* convert to # of bytes     */
      Bit.size  = Bit.rows * Bit.cols;      /* number of bytes in buffer */
    }
 
    Bit.bitmap = (void*)malloc( (int)Bit.size );
    if ( !Bit.bitmap )
      Panic( "ERROR: Not enough memory to allocate bitmap!\n" );
 
    ClearData();
  }
  
  static void  ClearData()
  {
    if ( gray_render )
      memset( Bit.bitmap, gray_palette[0], Bit.size );
    else
      memset( Bit.bitmap, 0, Bit.size );
  }
  

  static TT_Error  Reset_PtSize( int  pointSize )
  {
    TT_Error  error;
 
    if ( (error = TT_Set_Instance_CharSize( instance, pointSize*64 )) )
    {
      RestoreScreen();
      Message( "error = %d\n", error );
      Panic( "could not reset instance\n" );
    }

    TT_Get_Instance_Metrics( instance, &imetrics );
 
    xcenter = imetrics.x_ppem * xcenter_upem / units_per_em / 4;
    ycenter = imetrics.y_ppem * ycenter_upem / units_per_em / 4;

    xoffset = vio_Width/2  - xcenter;
    yoffset = vio_Height/2 - ycenter;

    return TT_Err_Ok;
  }


  static void Copy_Outline( TT_Outline*  from,
                            TT_Outline*  to )
  {
    int n;

    to->points   = from->points;
    to->contours = from->contours;

    n = from->points * sizeof(TT_F26Dot6 );

    memcpy( to->xCoord, from->xCoord, n );
    memcpy( to->yCoord, from->yCoord, n );
    memcpy( to->flag,   from->flag, from->points );
    
    n = from->contours * sizeof(short);

    memcpy( to->conEnds, from->conEnds, n );
  }


  static TT_Error  Reset_Rotation( int  rotation )
  { 
    /* TT_Error  error; */

    Copy_Outline( &org_outline, &outline );

    if ( rotation )
    {
      float  angle;

      TT_Set_Instance_Transform_Flags( instance, 1, 0 );

      angle = rotation * Pi / 512;

      matrix.xx = (TT_Fixed) (cos( angle ) * (1<<16));
      matrix.xy = (TT_Fixed) (sin( angle ) * (1<<16));
      matrix.yx = - matrix.xy;
      matrix.yy = matrix.xx;

      apply_matrix = 1;
    }
    else
      apply_matrix = 0;
 
    return TT_Err_Ok;
  }

  static TT_Error  LoadTrueTypeChar( int  idx, int  hint )
  {
    TT_Error  error;
    int       flags;
 
    flags = TTLOAD_SCALE_GLYPH;
    if ( hint )
      flags |= TTLOAD_HINT_GLYPH;
 
    error = TT_Load_Glyph( instance, glyph, idx, flags );
    if (!error && apply_matrix)
    {
      TT_Get_Glyph_Outline( glyph, &outline );
      TT_Translate_Outline( &outline, -xcenter*64, -ycenter*64 );
      TT_Transform_Outline( &outline, &matrix );
      TT_Translate_Outline( &outline, xcenter*64, ycenter*64 );
    }
    return error;
  }


  TT_Error  ConvertRaster()
  {
    if ( gray_render )
      return TT_Get_Glyph_Pixmap( glyph, &Bit, xoffset*64, yoffset*64 );
    else
      return TT_Get_Glyph_Bitmap( glyph, &Bit, xoffset*64, yoffset*64 );
  }


  int  Process_Event( TEvent*  event )
  {
    switch ( event->what )
    {
    case event_Quit:            /* ESC or q */
      return 0;
      
    case event_Keyboard:
      if ( event->info == 'h' ) /* Toggle hinting */
        hinted = !hinted;
      break;
 
    case event_Rotate_Glyph:
      rotation = ( rotation + event->info ) & 1023;
      break;

    case event_Scale_Glyph:
      ptsize += event->info;
      if ( ptsize < 1 )         ptsize = 1;
      if ( ptsize > MAXPTSIZE ) ptsize = MAXPTSIZE;
      break;

    case event_Change_Glyph:
      if ( use_cmap )
      {
        if ( event->info < 0 )
        {
          if ( Code > -event->info )
            Code += event->info;
          else
            Code = 0;

          for ( ; Code >= 0; Code-- )
          {
            Num = TT_Char_Index( char_map, Code );
            if ( Num > 0 )
              break;
          }
        }
        else
        {
          if ( Code < 65536 - event->info - 1 )
            Code += event->info;
          else
            Code = 65536 - 1;

          for ( ; Code < 65536; Code++ )
          {
            Num = TT_Char_Index( char_map, Code );
            if ( Num > 0 )
              break;
          }
        }
      }
      else
      {
        if ( event->info < 0 )
          if ( Num > -event->info )
            Num += event->info;
          else
            Num = 0;
        else
          if ( Num < num_glyphs - event->info - 1 )
            Num += event->info;
          else
            Num = num_glyphs - 1;
      }
      break;
    }

    return 1;
  }


  void usage( char*  execname )
  {
    Message( "\n" );
    Message( "ftzoom: simple TrueType glyph viewer - part of the FreeType project\n" );
    Message( "-------------------------------------------------------------------\n" );
    Message( "\n" );
    Message( "Usage: %s [options below] -fontname[.ttf|.ttc]\n", execname );
    Message( "\n" );
    Message( "  -g       gray-level rendering (default: none)\n" );
    Message( "  -p id    platform id (default: none)\n" );
    Message( "  -e id    encoding id (default: none)\n" );
    Message( "    If either -p or -e is not set, no cmap will be used.\n" );
    Message( "\n" );

    exit( 1 );
  }


  /* stack check dtp */

  int  main( int  argc, char**  argv ) 
  {
    int         i, xpos, ypos;
    int         platform = -1, encoding = -1;
    char        filename[128 + 4];
    char        alt_filename[128 + 4];
    char*       execname;
    int         option;
    TT_Error    error;
  
    TEvent  event;
 
    execname    = argv[0];
    gray_render = 0;

    while ( 1 )
    {
      option = getopt( argc, argv, "e:gp:" );

      if ( option == -1 )
        break;

      switch ( option )
      {
      case 'e':
        encoding = atoi( optarg );
        break;

      case 'g':
        gray_render = 1;
        break;

      case 'p':
        platform = atoi( optarg );
        break;

      default:
        usage( execname );
        break;
      }
    }

    if ( optind == argc )
      usage( execname );

    i = strlen( argv[optind] );
    while ( i > 0 && argv[optind][i] != '\\' && argv[optind][i] != '/' )
    {
      if ( argv[optind][i] == '.' )
        i = 0;
      i--;
    }

    filename[128] = '\0';
    alt_filename[128] = '\0';

    strncpy( filename, argv[optind], 128 );
    strncpy( alt_filename, argv[optind], 128 );

    if ( i >= 0 )
    {
      strncpy( filename + strlen( filename ), ".ttf", 4 );
      strncpy( alt_filename + strlen( alt_filename ), ".ttc", 4 );
    }

    if ( platform >= 0 || encoding >= 0 )
      use_cmap = 1;

    /* Initialization */
    TT_Init_FreeType( &engine );

    /* Load face */
 
    error = TT_Open_Face( engine, filename, &face );
 
    if ( error == TT_Err_Could_Not_Open_File )
    {
      strcpy( filename, alt_filename );
      error = TT_Open_Face( engine, alt_filename, &face );
    }
 
    if ( error == TT_Err_Could_Not_Open_File )
      Panic( "ERROR: could not find/open %s\n", filename );
    else if (error)
      Panic( "ERROR: while opening %s, error code = %x\n", filename, error );
 
    /* get face properties and allocate preload arrays */
 
    TT_Get_Face_Properties( face, &properties );
 
    num_glyphs   = properties.num_Glyphs;
    xcenter_upem = (properties.header->xMax - properties.header->xMin) / 2;
    ycenter_upem = (properties.header->yMax - properties.header->yMin) / 2;
    units_per_em = properties.header->Units_Per_EM;
 
    /* create glyph */
 
    error = TT_New_Glyph( face, &org_glyph );
    if ( error )
      Panic( "ERROR: could not create glyph container\n" );

    error = TT_New_Glyph( face, &glyph );
    if ( error )
      Panic( "ERROR: could not create glyph container\n" );
 
    /* create instance */
 
    error = TT_New_Instance( face, &instance );
    if ( error )
      Panic( "ERROR: could not create instance for %s\n", filename );
 
    error = TT_Set_Instance_Resolutions( instance, 96, 96 );
    if ( error )
      Panic( "ERROR: could not set device resolutions" );
 
    if ( gray_render )
    {
      if ( !SetGraphScreen( Graphics_Mode_Gray ) )
        Panic( "ERROR: could not set up grayscale graphics mode\n" );

      TT_Set_Raster_Gray_Palette( engine, gray_palette );
    }
    else
    {
      if ( !SetGraphScreen( Graphics_Mode_Mono ) )
        Panic( "ERROR: could not set up mono graphics mode\n" );
    }
 
    Init_Raster_Area();

    old_ptsize   = ptsize   = 150;
    old_rotation = rotation = 0;

    Reset_PtSize  ( ptsize );
    Reset_Rotation( rotation );

    if ( use_cmap )
    {
      int    num_cmap;
      short  cmap_plat;
      short  cmap_enc;

      num_cmap = TT_Get_CharMap_Count( face );
      for ( i = 0; i < num_cmap; i++ )
      {
        error = TT_Get_CharMap_ID( face, i, &cmap_plat, &cmap_enc );
        if ( error )
          Panic( "ERROR: cannot query cmap, error=%d\n", error );
        if ( cmap_plat == platform && cmap_enc == encoding )
          break;
      }

      if ( i == num_cmap )
        Panic( "ERROR: invalid platform and/or encoding ID\n" );

      error = TT_Get_CharMap( face, i, &char_map );
      if ( error )
        Panic( "ERROR: cannot load cmap, error=%d\n", error );

      num_glyphs = 1<<16;
    }

    Code     = 0;
    Num      = 0;
    autorun  = 0;
    Fail     = 0;
    xpos     = 0;
    ypos     = 0;

    for ( ;; )
    {
      ClearData();

      if ( ptsize != old_ptsize )
      {
        Reset_PtSize( ptsize );
        old_ptsize = ptsize;
      }

      if ( rotation != old_rotation )
      {
        Reset_Rotation( rotation );
        old_rotation = rotation;
      }

      if ( (error = LoadTrueTypeChar( Num, hinted )) == TT_Err_Ok )
      {
        ConvertRaster();
        if ( use_cmap )
          sprintf( Header, "glyph index = %3d, char code = 0x%x",
                   Num, Code );
        else
          sprintf( Header, "glyph index = %3d", Num );
      }
      else {
        Fail++;
        sprintf( Header, "glyph index = %3d, (ERROR) %x",
                 Num, error );
      }

      Display_Bitmap_On_Screen( Bit.bitmap, Bit.rows, Bit.cols ); 
#ifndef X11
#ifndef OS2
      Print_XY( 20, 0, Header );
#endif
#endif

#if 0
      if ( autorun )
      {
        xpos += cur_AdvanceWidth;
        if ( xpos + cur_AdvanceWidth > 640 )
        {
          xpos = 0;
          ypos += ( cur_Ascent - cur_Descent ) / 64;
          /* is == TRUETYPE DEFINED Precision */
        }

        Num = autorun++;
        if( !autorun )
        {
          xpos = 0;
          ypos += ( cur_Ascent - cur_Descent ) / 64;
          /* is == TRUETYPE DEFINED Precision */
        }
      }
      else
#endif
      {
        Get_Event( &event );
        if ( !Process_Event( &event ) ) goto Fin;
      }
    }

  Fin:

    RestoreScreen();

    TT_Done_FreeType( engine );

    Message( "fails = %d\n", Fail );

    return 0;
  }
