/* ufont.c */

#include "ufont.h"

#include "oslib/osfile.h"


#define MALLOC_CHUNK 256


struct ufont_font
{
  font_f handle[65536];
  char character[65536];
  unsigned int handles_used;
  font_f handles[256];
};


os_error error_exists = { error_FONT_NOT_FOUND, "Fonts file not found" };
os_error error_memory = { error_FONT_NO_ROOM, "Insufficient memory for font" };
os_error error_size = { error_FONT_BAD_FONT_FILE, "Wrong size of font file" };


/*
 *  UFont_FindFont
 *
 *  => as Font_FindFont, but
 *     font_name does not support '\' qualifiers
 *
 *  <= as Font_FindFont, but
 *     handle is 32-bit
 */

os_error *
xufont_find_font(char const *font_name,
                 int xsize,
                 int ysize,
                 int xres,
                 int yres,
                 struct ufont_font **font,
                 int *xres_out,
                 int *yres_out)
{
  char *old_font;
  char *fonts_file;
  char file_name[256];
  fileswitch_object_type obj_type;
  int code;
  int handle;
  unsigned int size;
  os_error *error;

  /* find size of Fonts file */
  strcpy(file_name, font_name);
  strcat(file_name, ".Fonts");
  error = xosfile_read_stamped_path(file_name, "UFont:", &obj_type, NULL, NULL, &size, NULL, NULL);
  if (error != NULL) return error;
  if (obj_type != fileswitch_IS_FILE) return &error_exists;

  /* allocate memory */
  fonts_file = malloc(size);
  if (fonts_file == NULL) return &error_memory;

  *font = malloc(sizeof(struct ufont_font));
  if (*font == NULL) return &error_memory;

  /* load Fonts */
  error = xosfile_load_stamped_path(file_name, fonts_file, "UFont:", NULL, NULL, NULL, NULL, NULL);
  if (error != NULL) return error;

  /* open all fonts listed in Fonts */
  for (old_font = fonts_file, handle = 0; old_font - fonts_file < size;
       old_font += strlen(old_font) + 1, handle++)
  {
/*     #ifdef DEBUG */
/*     printf("%i %s: ", handle, old_font); */
/*     #endif */

    error = xfont_find_font(old_font, xsize, ysize, xres, yres,
                            &(*font)->handles[handle], xres_out, yres_out);
    if (error != NULL) return error;

/*     #ifdef DEBUG */
/*     printf("%i\n", (*font)->handles[handle]); */
/*     #endif */
  }
  (*font)->handles_used = handle;

  /* free Fonts */
  free(fonts_file);

  /* load Data */
  strcpy(file_name, font_name);
  strcat(file_name, ".Data");
  error = xosfile_load_stamped_path(file_name, (byte *) (*font), "UFont:",
                                    NULL, NULL, NULL, &size, NULL);
  if (error != NULL) return error;
  if (size != 2*65536) return &error_size;

  /* change font indexes to font handles */
  for (code = 0; code < 65536; code++)
    (*font)->handle[code] = (*font)->handles[(*font)->handle[code]];

  return NULL;
}


/*
 *  UFont_LoseFont
 *
 *  => font handle as returned by UFont_FindFont
 */

os_error *
xufont_lose_font(struct ufont_font *font)
{
  unsigned int handle;
  os_error *error;

  /* close all fonts used */
  for (handle = 0; handle < font->handles_used; handle++)
  {
    error = xfont_lose_font(font->handles[handle]);
    if (error != NULL) return error;
  }

  /* free font structure */
  free(font);

  return NULL;
}


/*
 *  UFont_Paint
 *
 *  => font handle as returned by UFont_FindFont
 *     string is unicode (2 byte characters), see UFont_Convert
 *     other parameters as Font_Paint
 */

os_error *
xufont_paint(struct ufont_font *font,
             wchar_t const *string,
             font_string_flags flags,
             int xpos,
             int ypos,
             font_paint_block const *block,
             os_trfm const *trfm,
             unsigned int length)
{
  char *result;
  os_error *error;

  if ((flags & font_GIVEN_LENGTH) == 0) length = 0x7fffffff;

  error = xufont_convert(font, string, length, &result, NULL);
  if (error != NULL) return error;

  error = xfont_paint(result[1], result,
                      (flags & (~font_GIVEN_LENGTH)) | font_GIVEN_FONT,
                      xpos, ypos, block, trfm, 0);
  if (error != NULL) { free(result); return error; }

  free(result);

  return NULL;
}


/*
 *  UFont_Convert
 *
 *  => initial font
 *     unicode string to convert
 *     length to convert (characters)
 *
 *     control sequences in string (all 2 byte):
 *
 *        9, dx_low, dx_middle, dx_high   (each 2 byte, but maximum 255)
 *       11, dy_low, dy_middle, dy_high                 "
 *       19, r, g, b, R, G, B, max                      "
 *       25, underline_position, underline_thickness    "
 *       26, font_handle                  (4 bytes, ie. 2 wide characters)
 *       27, m1, m2, m3, m4               (each matrix entry 4 bytes,
 *       28, m1, m2, m3, m4, m5, m6        ie. 2 wide characters)
 *
 *  <= string converted to Font_Paint format
 *     table of offsets in unicode string
 */

os_error *
xufont_convert(struct ufont_font *font,
               wchar_t const *string,
               unsigned int length,
               char **presult,
               unsigned int **ptable)
{
  char *result;
  unsigned int string_index = 0;
  unsigned int result_index = 0;
  unsigned int i;
  unsigned int current_size = MALLOC_CHUNK;
  unsigned int *table;
  font_f current_font = 0;

  result = malloc(MALLOC_CHUNK);
  if (result == NULL) return &error_memory;

  if (ptable != NULL)
  {
    table = malloc(MALLOC_CHUNK * sizeof(unsigned int));
    if (table == NULL) return &error_memory;
  }

  while ((string[string_index] != 0) && (string_index < length))
  {
/*     #ifdef DEBUG */
/*     printf("0x%x ", string[string_index]); */
/*     #endif */

    if (result_index + 32 > current_size)
    {
      current_size += MALLOC_CHUNK;

      result = realloc(result, current_size);
      if (result == NULL) return &error_memory;

      if (ptable != NULL)
      {
        table = realloc(result, current_size * sizeof(unsigned int));
        if (table == NULL) return &error_memory;
      }
    }

    if (ptable != NULL) table[result_index] = string_index;

    switch (string[string_index])
    {
      case 9:
      case 11:
      {
        if (ptable != NULL)
          table[result_index + 1] = table[result_index + 2] = table[result_index + 3] = string_index;

        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        break;
      }
      case 19:
      {
        if (ptable != NULL)
          table[result_index + 1] = table[result_index + 2] = table[result_index + 3] =
          table[result_index + 4] = table[result_index + 5] = table[result_index + 6] =
                                                              table[result_index + 7] = string_index;

        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        break;
      }
      case 25:
      {
        if (ptable != NULL)
          table[result_index + 1] = table[result_index + 2] = string_index;

        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        result[result_index++] = string[string_index++];
        break;
      }
      case 26:
      {
        string_index++;
        font = (struct ufont_font *) ((string[string_index + 1] << 16) | string[string_index]);
        break;
      }
      case 27:
      {
        if (ptable != NULL)
          for (i = result_index + 1; i < ((result_index + 1 + 16 + 3) & (unsigned int) (~3)); i++)
            table[i] = string_index;

        result[result_index++] = string[string_index++];
        result_index = (result_index + 3) & (unsigned int) (~3);
        for (i = 0; i < 8; i++)
        {
          result[result_index++] = string[string_index] & 0xff;
          result[result_index++] = string[string_index++] >> 8;
        }
        break;
      }
      case 28:
      {
        if (ptable != NULL)
          for (i = result_index + 1; i < ((result_index + 1 + 24 + 3) & (unsigned int) (~3)); i++)
            table[i] = string_index;

        result[result_index++] = string[string_index++];
        result_index = (result_index + 3) & (unsigned int) (~3);
        for (i = 0; i < 12; i++)
        {
          result[result_index++] = string[string_index] & 0xff;
          result[result_index++] = string[string_index++] >> 8;
        }
        break;
      }
      default:
      {
        if (font->handle[(unsigned int) string[string_index]] != current_font)
        {
          if (ptable != NULL)
            table[result_index + 1] = table[result_index + 2] = string_index;

          result[result_index++] = 26;
          result[result_index++] = current_font = font->handle[(unsigned int) string[string_index]];

/*           #ifdef DEBUG */
/*           printf("{%i} ", current_font); */
/*           #endif */
        }
        result[result_index++] = font->character[(unsigned int) string[string_index++]];

/*         #ifdef DEBUG */
/*         printf("[0x%x] ", result[result_index - 1]); */
/*         #endif */

        break;
      }
    }
  }
  result[result_index] = 0;

  *presult = result;
  if (ptable != NULL)
  {
    table[result_index] = string_index;
    *ptable = table;
  }

/*   #ifdef DEBUG */
/*   printf("\n"); */

/*   for (result_index = 0; result[result_index] != 0; result_index++) */
/*     printf("0x%x ", result[result_index]); */
/*   printf("\n"); */
/*   #endif */

  return NULL;
}


/*
 *  UFont_ScanString
 *
 *  => font handle as returned by UFont_FindFont
 *     string is unicode (2 byte characters), see UFont_Convert
 *     split length is index in string, not pointer
 *     other parameters as Font_ScanString
 *
 *  <= as Font_ScanString
 */

extern os_error *
xufont_scan_string(ufont_f font,
                   wchar_t const *string,
                   font_string_flags flags,
                   int x,
                   int y,
                   font_scan_block const *block,
                   os_trfm const *trfm,
                   unsigned int length,
                   int *split_length,
                   int *x_out,
                   int *y_out,
                   int *length_out)
{
  char *result;
  char *split_point;
  unsigned int *table;
  os_error *error;

  if ((flags & font_GIVEN_LENGTH) == 0) length = 0x7fffffff;

  error = xufont_convert(font, string, length, &result, &table);
  if (error != NULL) return error;

/*   #ifdef DEBUG */
/*   { */
/*     unsigned int i; */
/*     for (i = 0; i < 200; i++) */
/*       printf("%i ", table[i]); */
/*     printf("\n"); */
/*   } */
/*   #endif */

  error = xfont_scan_string((font_f) result[1], result,
                      (flags & (~font_GIVEN_LENGTH)) | font_GIVEN_FONT,
                      x, y, block, trfm, 0,
                      &split_point, x_out, y_out, length_out);
  if (error != NULL) { free(result); return error; }

  if (split_length != NULL)
    *split_length = table[split_point - result];

  free(result);
  free(table);

  return NULL;
}
