/*  gfxfont.cpp
    Font handling functions

    28.06.2002: tk, initial implementation.

    Copyright (c)2001 by Thomas Kindler, thomas.kindler@gmx.de

    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. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/

// include files -----
//
#include "gfxfont.h"
#include "gfxcore.h"
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <mem.h>
#include <dos.h>


static char printfbuf[256];


/**
 * Load a Font from disk.
 *
 * \param   fn  filename to load.
 * \return  pointer to Font structure, or
 *          NULL if some error occured.
 */
Font *GFX_LoadFont(char *fn)
{
  unsigned long magic, size;
  Font *font = NULL;
  FILE *file = NULL;

  printf("loading \"%s\".. ", fn);

  file = fopen(fn, "rb");
  if (!file) {
    perror("");
    goto error;
  }

  fread(&magic, sizeof(magic), 1, file);
  fread(&size,  sizeof(size),  1, file);

  if (magic != FONT_MAGIC) {
    printf("invalid format.\n");
    goto error;
  }

  font = (Font*)malloc(size);
  if (!font) {
    printf("out of memory.\n");
    goto error;
  }
  fread(font, size, 1, file);
  fclose(file);

  printf( "%dx%d, %d-%d (%d bytes).\n",
          font->maxWidth, font->height, font->firstChar,
          font->firstChar+font->numChars, size );
  return  font;

error:
  if (font) free(font);
  if (file) fclose(file);
  return  NULL;
}


/**
 * Free a Font.
 *
 * \param  font  pointer to Font structure to free.
 */
void GFX_FreeFont(Font *font)
{
  if (font)
    free(font);
}


/**
 * Print a string into scratch memory.
 *
 * \param   bmBits  pointer to scratch memory
 * \param   fnt     pointer to bitmap font
 * \param   string  text to print
 * \return  width of printed text
 */
#pragma  warn -rvl  // ignore compiler warning (return value is in ax)
static int printScratch(char *bmBits, Font *font, char *string)
{
  int    pages = font->numPages;

  asm {
        push  bx
        push  cx
        push  dx
        push  si
        push  di
        push  ds
        push  es

        xor   bx, bx
        xor   dx, dx

        // es:di points to destination bitmap
        //
        les   di, dword ptr bmBits

copypage:
        push  word ptr[string]    // save string pointer
        mov   ax, di              // save beginning-of-page

nextchar:
        lds   si, [string]        // fetch character
        inc   word ptr [string]
        xor   bx, bx
        mov   bl, [si]

        lds   si, [font]
        or    bx, bx              // == 0?
        je    nextpage
        sub   bx, [si+0]          // < firstChar?
        jl    nextchar
        cmp   bx, [si+2]          // >= numChars?
        jge   nextchar

        add   bx, bx              // bx = (char*2)+12
        add   bx, 12

        mov   cx, [si+bx+2]       // get near pointer to (char+1)
        sub   cx, [si+bx]         // calculate width
        add   si, [si+bx]         // get near pointer to char

        rep   movsb               // copy char
        jmp   nextchar

nextpage:
        pop   word ptr[string]    // reset string pointer

        add   dx, [si+8]          // advance to next page
        sub   pages, 1            //
        jne   copypage

        sub   di, ax              // return width of text
        mov   ax, di              //

        pop   es
        pop   ds
        pop   di
        pop   si
        pop   dx
        pop   cx
        pop   bx
  }
}
#pragma  warn .rvl


/*
 * Print a text string at the current cursor position.
 * The x-cursor position is moved to the end of the string,
 * the y position doesn't get modified (no wrapping!)
 *
 * \param  gfx  Graphics context to use.
 * \param  string  pointer to text string.
 * \return width of printed text (in pixels).
 */
int GFX_Print(Graphics *gfx, char *string)
{
  _gfx_scratch->width  = printScratch(_gfx_scratch->bmBits, gfx->font, string);
  _gfx_scratch->height = gfx->font->height;
  assert(
    _gfx_scratch->width*(_gfx_scratch->height+7)/8 < _gfx_scratch->bmBitsSize
  );

  BM_Blit(
    gfx->bitmap, gfx->x, gfx->y,
    _gfx_scratch, 0, 0, -1, -1,
    gfx->blitMode
  );

  gfx->x += _gfx_scratch->width;
  return  _gfx_scratch->width;
}


/**
 * Print text at position (x,y).
 * The cursor position isn't changed by this function.
 *
 * \param  gfx  Graphics context to use.
 * \param  string  pointer to text string.
 * \return width of printed text (in pixels).
 */
int GFX_PrintAt(Graphics *gfx, int x, int y, char *string)
{
  swap(gfx->x, x);  swap(gfx->y, y);
  int width = GFX_Print(gfx, string);
  swap(gfx->x, x);  swap(gfx->y, y);
  return width;
}


/**
 * Print a formatted text string. The cursor
 * position is advanced to the end of the string.
 *
 * \param   gfx  Graphics context to use.
 * \param   format  see printf documentation.
 *
 * \return  width of printed text (in pixels).
 */
int GFX_Printf(Graphics *gfx, char *format, ...)
{
  int  width;

  va_list arglist;
  va_start(arglist, format);

  int ret = vsprintf(printfbuf, format, arglist);
  assert(ret < sizeof(printfbuf));

  width = GFX_Print(gfx, printfbuf);
  va_end(arglist);

  return  width;
}


/**
 * Print formatted text at position (x,y).
 * The cursor position isn't changed by this function.
 *
 * \param   gfx  Graphics context to use.
 * \param   format  see printf documentation.
 *
 * \return  width of printed text (in pixels).
 */
int GFX_PrintfAt(Graphics *gfx, int x, int y, char *format, ...)
{
  int  width;

  va_list arglist;
  va_start(arglist, format);

  int ret = vsprintf(printfbuf, format, arglist);
  assert(ret < sizeof(printfbuf));

  width = GFX_PrintAt(gfx, x, y, printfbuf);
  va_end(arglist);

  return  width;
}


/**
 * Get width of text.
 * Return the width of the string in pixels, as it
 * would be returned by GFX_Print().
 *
 * \param   gfx  graphics context to use.
 * \param   string text string
 * \return  width in pixels
 */
int GFX_TextWidth(Graphics *gfx, char *string)
{
  int  width = 0;
  Font *font = gfx->font;

  while (char c = *string++) {
    c -= font->firstChar;
    if (c < 0) continue;
    if (c >= font->numChars)  continue;
    width += font->offsets[c+1] - font->offsets[c];
  }

  return width;
}

