/**
 *
 * ChoX11: XLib replacement for RISC OS
 *
 * Font handling
 *
 * Copyright 2003 by Peter Naulls
 * Written by Peter Naulls and Alan Buckley
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation. No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 */


#define XLIB_ILLEGAL_ACCESS
#define NeedFunctionPrototypes 1
#define NeedNestedPrototypes   1

#include <X11/Xlib.h>
#include <X11/Xutil.h> 

#include <stdio.h>
#include <ctype.h>
#include <kernel.h>

#include "DeskLib:Font.h"

#include "chox11.h"

/* Mapping of common font names to RISC OS equivalents */
#define MAX_FONT_NAME_ALIAS 6

struct {
   const char *x11_name;
   const char *ro_name;
} FontNameAlias[MAX_FONT_NAME_ALIAS] = {
  { "*", "Corpus"}, /* Wildcard will find corpus first */
  { "fixed", "Corpus" },
  { "courier", "Corpus"},
  { "lucidatypewriter", "Corpus"}, 
  { "helvetica", "Homerton"},
  { "times", "Trinity"}
};


/**
   Fonts are reference counted so they are not deleted when
   they are still in use in the drawing queue
*/
void Chox11Font_AddRef(Chox_Font *font) {
  if (font) {
    font->ref_count++;
  }
}

void Chox11Font_Release(Chox_Font *font) {

  if (font && --(font->ref_count) <= 0) {
     Font_LoseFont(font->handle);
     printf("Chox11Font_Release: %s\n", font->ro_name);
     free(font->ro_name);
     free(font);
  }
}


static _Xconst char *ChoxFont_ParseComponent(_Xconst char **position, unsigned int *length) {
  _Xconst char *start;

  if (**position == '-')
    (*position)++;

  start = *position;

  while (**position != '\0' && **position != '-')
    (*position)++;

  *length = *position - start;

  printf("component: \"%s\"\n", start);

  return start;
}



static Bool ChoxFont_ParseFontName(_Xconst char *name, Chox_Font *font) {
   int width = 0;
   int height = 0;
   int pixel_height = 0;
   int ave_width = 0;
   int xdpi = 0, ydpi = 0;
   char ro_name[64];
   char weight[32];
   char slant = 'r';
   char set_width[32];
   _Xconst char *char_set = "";
   char spacing = 'm';

   ro_name[0] = 0;
   weight[0] = 0;
   set_width[0] = 0;

   printf("Parse font: orignal name %s\n", name);

  /* Check that it's a complex specifier */
  if (strchr(name, '-')) {
    _Xconst char *parse = name;
    _Xconst char *field;
    unsigned int length;

    printf("complex font name: ");

    /* Foundry - ignored */
    ChoxFont_ParseComponent(&parse, &length);
    /* Font family */
    field = ChoxFont_ParseComponent(&parse, &length);
    strncpy(ro_name, field, length);
    ro_name[length] = 0;

    /* Weight */
    field = ChoxFont_ParseComponent(&parse, &length);
    strncpy(weight, field, length);
    weight[length] = 0;

    slant = *ChoxFont_ParseComponent(&parse, &length);

    /* Set Width */
    field = ChoxFont_ParseComponent(&parse, &length);
    strncpy(set_width, field, length);
    set_width[length] = 0;

    ChoxFont_ParseComponent(&parse, &length);
    /* Pixels */
    pixel_height = atoi(ChoxFont_ParseComponent(&parse, &length));
    /* Points (1/10ths point) - scale by 3/4 for RISC OS sizes */
    height =  atoi(ChoxFont_ParseComponent(&parse, &length));
    /* Horz DPI */
    xdpi = atoi(ChoxFont_ParseComponent(&parse, &length));
    /* Vert DPI */
    ydpi = atoi(ChoxFont_ParseComponent(&parse, &length));

    /* Spacing */
    spacing = *ChoxFont_ParseComponent(&parse, &length);
    /* Average width (1/10ths pixel) */
    ave_width = atoi(ChoxFont_ParseComponent(&parse, &length));
    /* Character set */
    char_set = ChoxFont_ParseComponent(&parse, &length);
  } else {
    /* Check for simple names and sizes */
    if (strcmp(name, "fixed") == 0) {
      strcpy(set_width, "semicondensed");
      spacing = 'c';
      height = 120;
    } else if (strcmp(name, "variable") == 0) {
      spacing = 'p';
      height = 120;
      strcpy(weight, "bold");
    } else if (strchr(name, 'x')) {
      /* "widthxheight" */
      ave_width = atoi(name) * 10;
      pixel_height = atoi(strchr(name, 'x') + 1);
      height = 0;
    } else {
      return False;
    }
  }

  printf("Parse font: parsed fields --%s-%s-%c-%s--%d-%d-%d-%d-%c-%d-%s\n", 
          ro_name, weight, slant, set_width,
          pixel_height, height, xdpi, ydpi,
          spacing, ave_width, char_set);

  /* Calculate defaults */
  if (xdpi == 0) xdpi = 75;
  if (ydpi == 0) ydpi = 75;

  if (height == 0) {
    if (pixel_height != 0) {
      height = pixel_height * 10;
    } else {
      height = 15 * 16;
    }
  }

  if (ave_width != 0) {
    width = ave_width * 2; /* Factor to convert to 1/16th points */
  } else {
    width = height;
    if (strcmp(set_width, "semicondensed") == 0) width /= 2;
  }

  if (ro_name[0] == 0) {
     // Derive name from other attributes
     if (spacing == 'p') strcpy(ro_name, "Homerton");
     else strcpy(ro_name, "Corpus");
  } else {
     int alias;

     for (alias = 0; alias < MAX_FONT_NAME_ALIAS; alias++) {
       if (strcmp(ro_name, FontNameAlias[alias].x11_name) == 0) {
         strcpy(ro_name, FontNameAlias[alias].ro_name);
         break;
       }
     }
  }

  if (weight[0] == 0 || strcmp(weight, "*") == 0)
     strcpy(weight,"Medium");

  printf("Parse font: Calculated fields --%s-%s-%c-%s--%d-%d-%d-%d-%c-%d-%s\n",
          ro_name, weight, slant, set_width,
          pixel_height, height, xdpi, ydpi,
          spacing, ave_width, char_set);

  strcat(ro_name, ".");
  strcat(ro_name, weight);

  if (slant == 'o')
    strcat(ro_name,".Oblique");
  else if (slant == 'i')
    strcat(ro_name,".Italic");

  /* Finally scale width and height for RISC OS DPI and to 16ths of an inch */
  font->ro_name = strdup(ro_name);
  font->width   = width * 16 / 10;
  font->height  = height * 16 / 10;
  printf("Parse font: RISC OS name and size: %s %d %d\n", ro_name, font->width, font->height);

  return True;
}


extern Font XLoadFont(

    Display*		 display,
    _Xconst char*	 name

) {
  Chox_Font *font = calloc(sizeof(Chox_Font), 1);
  os_error *error;

  if (!ChoxFont_ParseFontName(name, font)) {
    printf("XLoadFont: failed to parse font name %s\n", name);
    free(font->ro_name);
    free(font);
    return BadName;
  }

  if ((error = Font_FindFont(&font->handle, font->ro_name, font->width, font->height, 0, 0))) {
    printf("XLoadFont: couldn't get RISC OS font %s (requested name %s): %s\n", font->ro_name, name, error->errmess);
    free(font->ro_name);
    free(font);
    return BadName;
  }

  font->ref_count = 1;
  printf("XLoadFont: %s (using RISC OS font %s) returning %p\n", name, font->ro_name, font);

  return (Font)font;
}


/**
 * Experimental code to get the full metrics
 */
static XFontStruct *ReadFontMetrics(XFontStruct *metrics, Chox_Font *font) {
  _kernel_swi_regs regs;
  int *bbox = NULL;
  char *misc = NULL;

  metrics->per_char = calloc(sizeof(XCharStruct), 224);

  metrics->min_bounds.width   = 32767;
  metrics->min_bounds.ascent  = 32767;
  metrics->min_bounds.descent = 0;

  metrics->max_bounds.width   = 0;
  metrics->max_bounds.ascent  = 0;
  metrics->max_bounds.descent = 0;

  regs.r[0] = font->handle;
  regs.r[1] = 0;  /* pointer to buffer for bounding box information, or 0 to read size of data */
  regs.r[2] = 0;  /* pointer to buffer for x width information, or 0 to read size of data */
  regs.r[3] = 0;  /* pointer to buffer for y width information, or 0 to read size of data */
  regs.r[4] = 0;  /* pointer to buffer for miscellaneous information, or 0 to read size of data */
  regs.r[5] = 0;  /* pointer to buffer for kerning information, or 0 to read size of data */
  regs.r[6] = 0;
  regs.r[7] = 0;

  if (_kernel_swi(0x4009F, &regs, &regs) != NULL) {
    puts("Font_ReadFontMetrics call to get sizes failed\n");
    free(metrics->per_char);
    free(metrics);
    return NULL;
  }

  if (regs.r[1]) {
    bbox = (int *)malloc(regs.r[1]);
    regs.r[1] = (int)bbox;
  }

  if (regs.r[4]) {
    misc = malloc(regs.r[4]);
    regs.r[4] = (int)misc;
  }

  regs.r[0] = font->handle;
  regs.r[2] = regs.r[3] = regs.r[5] = regs.r[6] = regs.r[7] = 0;

  if (_kernel_swi(0x4009F, &regs, &regs) != NULL) {
    puts("Font_ReadFontMetrics call to get data failed\n");
    free(metrics->per_char);
    free(metrics);
    return NULL;
  }

  if (misc) {
    int descent = -*(int *)(misc + 40);
    int ascent  = *(int *)(misc + 44);

    metrics->ascent = ascent / 800 + ((ascent % 800) != 0); /* 800 = 400 millipoints per os unit, 2 os units per pixel */
    metrics->descent = descent / 800 + ((descent %800) != 0);
  }

  if (bbox) {
    int *charbox = bbox + 32 * 4;
    int width, ascent;
    int letter;

    for (letter = 0; letter < 224; letter++) {
      width = charbox[2] - charbox[0];
      ascent = charbox[3] - charbox[1];
      metrics->per_char[letter].width  = width / 800 + ((width % 800) !=0);
      metrics->per_char[letter].ascent = ascent/ 800 + ((ascent % 800) !=0);

      printf("Char %c: %d,%d,%d,%d, pixels %dx%d\n", (char)(letter+32), charbox[0], charbox[1], charbox[2], charbox[3], metrics->per_char[letter].width, metrics->per_char[letter].ascent);

      if (metrics->per_char[letter].width && metrics->per_char[letter].ascent) {
        if (metrics->per_char[letter].width < metrics->min_bounds.width)
          metrics->min_bounds.width = metrics->per_char[letter].width;

        if (metrics->per_char[letter].ascent < metrics->min_bounds.ascent)
          metrics->min_bounds.ascent = metrics->per_char[letter].ascent;

        if (metrics->per_char[letter].width > metrics->max_bounds.width)
          metrics->max_bounds.width = metrics->per_char[letter].width;

        if (metrics->per_char[letter].ascent > metrics->max_bounds.ascent)
          metrics->max_bounds.ascent = metrics->per_char[letter].ascent;
      }
      charbox += 4;
    }
  }

  free(bbox);
  free(misc);

  return metrics;
}


extern XFontStruct *XQueryFont(

    Display*		 display,
    XID			 font_ID

) {
  Chox_Font *font = (Chox_Font *)font_ID;
  XFontStruct *metrics = calloc(sizeof(XFontStruct), 1);
  font_info info;

  if (!font) return NULL;

  Font_ReadInfo(font->handle, &info);

  printf("XQueryFont: returned rid = %p \n", metrics);

  metrics->fid = font_ID;
  Chox11Font_AddRef(font);

  metrics->direction = FontLeftToRight;
  metrics->min_char_or_byte2 = 32;
  metrics->max_char_or_byte2 = 255;

  metrics->min_bounds.width   = (info.maxx - info.minx) / 2;
  metrics->min_bounds.ascent  = info.maxy / 2;
  metrics->min_bounds.descent = -info.miny / 2;

  metrics->max_bounds.width   = (info.maxx - info.minx) / 2;
  metrics->max_bounds.ascent  = info.maxy / 2;
  metrics->max_bounds.descent = -info.miny / 2;

  metrics->ascent = metrics->max_bounds.ascent;
  metrics->descent = metrics->max_bounds.descent;

  printf("XQueryFont: %x ascent %d, descent %d, width: min %d, max %d, ascent: min %d, max %d\n", metrics->fid, metrics->ascent, metrics->descent,metrics->min_bounds.width, metrics->max_bounds.width, metrics->min_bounds.ascent, metrics->max_bounds.ascent);

/* TODO: Read complete metrics for the font */
//  metrics = ReadFontMetrics(metrics, font);

  printf("XQueryFont: %x ascent %d, descent %d, width: min %d, max %d, ascent: min %d, max %d\n", metrics->fid, metrics->ascent, metrics->descent,metrics->min_bounds.width, metrics->max_bounds.width, metrics->min_bounds.ascent, metrics->max_bounds.ascent);

  return metrics;
}


extern XFontStruct *XLoadQueryFont(

    Display*		 display,
    _Xconst char*	 name

) {
  Font font;

  printf("XLoadQueryFont: name = %s\n", name);

  font = XLoadFont(display, name);

  if (font != BadName) {
    XFontStruct *fs = XQueryFont(display, font);
    Chox11Font_Release((Chox_Font *)font);
    return fs;
  }

  return NULL;
}


extern char **XListFonts(

    Display*		 display,
    _Xconst char*	 pattern,
    int			 maxnames,
    int*		 actual_count_return

) {
  int count;
  char name[40];
  char *p;
  char *weight, *slant;
  char x11name[128];
  char **x11names = malloc(sizeof(char *));
  int total = 0;

  puts("XListFonts (not implemented)");

  while (Font_ListFonts(name, &count) == NULL && count != -1 && total < maxnames) {
    strcpy(x11name,"-riscos-");
    p = name;

    while (*p) {
       *p = tolower(*p);
       p++;
     }

    weight = strchr(name,'.');

    if (weight) {
       *weight++ = 0;
       strcat(x11name, name);
       strcat(x11name, "-");

       slant = strchr(weight, '.');
       if (slant) *slant++ = 0;
       strcat(x11name, weight);
       strcat(x11name, "-");

       if (slant) {
          if (strcmp(slant,"oblique") == 0)
            strcat(x11name,"o");
          else if (strcmp(slant, "italic") == 0)
            strcat(x11name, "i");
          else
            strcat(x11name,"r");

       } else {
         strcat(x11name,"r");
       }

     } else {
       strcat(x11name, name);
       strcat(x11name,"-medium-r");

     }

     strcat(x11name, "-normal--0-0-0-0-");
          strcat(x11name,"p"); /*TODO: detect if font is proportional or monospaced */
          strcat(x11name, "-0-iso8859-1");

     x11names = realloc(x11names, sizeof(char *) * (total+2));
     x11names[total] = strdup(x11name);
     total++;
  }

  x11names[total] = 0;

  if (actual_count_return) *actual_count_return = total;

  return x11names;
}


extern char **XListFontsWithInfo(

    Display*		 display,
    _Xconst char*	 pattern,
    int			 maxnames,
    int*		 count_return,
    XFontStruct**	 info_return

);


extern char **XGetFontPath(

    Display*		 display,
    int*		 npaths_return

);


extern XFreeFont(

    Display*		 display,
    XFontStruct*	 font_struct

) {
  Chox11Font_Release((Chox_Font *)font_struct->fid);
  free(font_struct);
}


extern XFreeFontInfo(

    char**		 names,
    XFontStruct*	 free_info,
    int			 actual_count

) {
  XFontStruct *release_struct = free_info;

  if (names) XFreeFontNames(names);

  while (actual_count--) {
    Chox11Font_Release((Chox_Font *)release_struct->fid);
    release_struct++;
  }

  free(free_info);
}
       
extern XFreeFontNames(

    char**		 list

) {
  int i = 0;
  while (list[i]) free(list[i++]);
  free(list);

  return Success;
}


extern XFreeFontPath(

    char**		 list

);


extern Bool XGetFontProperty(

    XFontStruct*	 font_struct,
    Atom		 atom,
    unsigned long*	 value_return

) {
  printf("XGetFontProperty: atom=%x (not implemented)\n", atom);
  *value_return = 0;
  return True;
}


extern XDrawString(
    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst char*	 string,
    int			 length
) {
  Chox11Queue_DrawString(display, d, gc, x, y, string, length, False);
}


extern XDrawString16(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst XChar2b*	 string,
    int			 length

) {
 puts("XDrawString16 (not implemented)");
}


XDrawImageString(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst char*	 string,
    int			 length

) {
  puts("XDrawImageString");
  Chox11Queue_DrawString(display, d, gc, x, y, string, length, True);
}


XDrawImageString16(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst XChar2b*	 string,
    int			 length

) {
 puts("XDrawImageString16 (not implemented)");
}


extern XDrawText(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    XTextItem*		 items,
    int			 nitems

);

extern XDrawText16(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    XTextItem16*	 items,
    int			 nitems

);


void XmbDrawImageString(

    Display*		 display,
    Drawable		 d,
    XFontSet		 font_set,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst char*	 text,
    int			 bytes_text

);

void XwcDrawImageString(

    Display*		 display,
    Drawable		 d,
    XFontSet		 font_set,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst wchar_t*	 text,
    int			 num_wchars

) {
  puts("XwcDrawImageString (not implemented)");
}


extern XQueryTextExtents(

    Display*		 display,
    XID			 font_ID,
    _Xconst char*	 string,
    int			 nchars,
    int*		 direction_return,
    int*		 font_ascent_return,
    int*		 font_descent_return,
    XCharStruct*	 overall_return    

);

extern XQueryTextExtents16(

    Display*		 display,
    XID			 font_ID,
    _Xconst XChar2b*	 string,
    int			 nchars,
    int*		 direction_return,
    int*		 font_ascent_return,
    int*		 font_descent_return,
    XCharStruct*	 overall_return

);


extern XSetFont(

    Display*		 display,
    GC			 gc,
    Font		 font

) {
  XGContext *xgc = (XGContext *)gc->gid;

  if (!font) return BadFont;

  xgc->values.font = font;
  printf("XSetFont to %x (%s %dx%d)\n", font, ((Chox_Font *)font)->ro_name, ((Chox_Font *)font)->width, ((Chox_Font *)font)->height);
  return Success;
}


extern XSetFontPath(

    Display*		 display,
    char**		 directories,
    int			 ndirs	     

);


extern XUnloadFont(

    Display*		 display,
    Font		 font

) {
  printf("XUnloadFont %x, %s\n", (int)font, ((Chox_Font*)font)->ro_name);
  Chox11Font_Release((Chox_Font *)font);
}


/*extern XFontSet XCreateFontSet(

    Display*		 display,
    _Xconst char*	 base_font_name_list,
    char***		 missing_charset_list,
    int*		 missing_charset_count,
    char**		 def_string

) {
  XFontSet *ptr = calloc(4, 1);

  printf("XCreateFontSet: base_font_name_list='%s' returning id %x (not implemented)\n", base_font_name_list, (int)ptr);

  *missing_charset_list = NULL;
  *missing_charset_count = 0;
  *def_string = NULL;

  return (XFontSet)ptr;
}

extern void XFreeFontSet(

    Display*		 display,
    XFontSet		 font_set

) {
  free(font_set);
}

extern int XFontsOfFontSet(

    XFontSet		 font_set,
    XFontStruct***	 font_struct_list,
    char***		 font_name_list

) {
 puts("XFontsOfFontSet (not implemented)");
 return 0;
}

extern char *XBaseFontNameListOfFontSet(

    XFontSet		 font_set

) {
  puts("XBaseFontNameListOfFontSet (not implemented)");
  return strdup("font");
}

extern char *XLocaleOfFontSet(

    XFontSet		 font_set

);*/


extern int XwcTextEscapement(

    XFontSet		 font_set,
    _Xconst wchar_t*	 text,
    int			 num_wchars

) {
 puts("XwcTextEscapement (not implemented)");
 return 0;
}

extern int XwcTextExtents(

    XFontSet		 font_set,
    _Xconst wchar_t*	 text,
    int			 num_wchars,
    XRectangle*		 overall_ink_return,
    XRectangle*		 overall_logical_return

) {
 puts("XwcTextExtents (not implemented)");
 return 0;
}

extern Status XmbTextPerCharExtents(

    XFontSet		 font_set,
    _Xconst char*	 text,
    int			 bytes_text,
    XRectangle*		 ink_extents_buffer,
    XRectangle*		 logical_extents_buffer,
    int			 buffer_size,
    int*		 num_chars,
    XRectangle*		 overall_ink_return,
    XRectangle*		 overall_logical_return

);

extern Status XwcTextPerCharExtents(

    XFontSet		 font_set,
    _Xconst wchar_t*	 text,
    int			 num_wchars,
    XRectangle*		 ink_extents_buffer,
    XRectangle*		 logical_extents_buffer,
    int			 buffer_size,
    int*		 num_chars,
    XRectangle*		 overall_ink_return,
    XRectangle*		 overall_logical_return

);

extern void XmbDrawText(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    XmbTextItem*	 text_items,
    int			 nitems

);

extern void XwcDrawText(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    int			 x,
    int			 y,
    XwcTextItem*	 text_items,
    int			 nitems

);


extern void XwcDrawString(

    Display*		 display,
    Drawable		 d,
    XFontSet		 font_set,
    GC			 gc,
    int			 x,
    int			 y,
    _Xconst wchar_t*	 text,
    int			 num_wchars

) {
  puts("XwcDrawString (not implemented)");
}


/*
 * XTextWidth - compute the width of a string of eight bit bytes.  This is a 
 * subset of XTextExtents.
 */
int XTextWidth (
    XFontStruct *fs,
    _Xconst char *string,
    int count)
{
  Chox_Font *font;
  char *end;
  int x = 0x7fffffff;
  int y = 0x7fffffff;
  unsigned int flags = font_plot_GIVENHANDLE | font_STRLEN | font_scan_BBOX;
  font_scanstringblock block;
  int minx, maxx;

  printf("XTextWidth: %p\n", fs); 

  if (!fs || !fs->fid)
    return 0;

  font = (Chox_Font *)fs->fid;
  printf("XTextWidth: %p\n", font); 

  block.offsets.space.x  = 0;
  block.offsets.space.y  = 0;
  block.offsets.letter.x = 0;
  block.offsets.letter.y = 0;
  block.split_ch = -1; /* No split char */

  Font_ScanString(font->handle, string, &end, flags, &x,&y, &block, NULL, &count);

  minx = block.bbox.min.x / 400;
  maxx = block.bbox.max.x / 400;

  return (maxx - minx) / 2;
}
