#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "baricon.h"
#include "res.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "werr.h"
#include "xfersend.h"
#include "saveas.h"
#include "flex.h"
#include "heap.h"


#ifdef COMPRESS

#define _COMPRESS 1

#else

#define _COMPRESS 0

#endif


#define UNUSED(x) ((x) = (x))

#define icon_menu_INFO  1
#define icon_menu_QUIT  2

/******************************* GLOBAL DATA *******************************/

char *charnames[256];

typedef struct {
  char ident[4];
  char bpp;
  char version;
  short dsize;

  short x0;
  short y0;
  short xd;
  short yd;

  int offset[9];
} font_hdr;

typedef struct {
  char name[40];
  int  rsvd1;
  int  rsvd2;
  char chars;
  char rsvd[3];

  char mapping[256];
} metrics_hdr;

char buff[256];

wimp_i baricon_handle;

menu icon_menu;

int xy_bits;
float xy_scale;

/******************************** FUNCTIONS ********************************/

void null_proc(wimp_i icon)
{
  UNUSED(icon);
}

int sgn8(int n)
{
  return (n & 0x0000080) ? (n | 0xffffff00) : n;
}

int sgn12(int n)
{
  return (n & 0x00000800) ? (n | 0xfffff000) : n;
}

void fprintxy(FILE *fp, char **p)
{
int x,y;
int d;

  switch (xy_bits)
  {
  case 8:
    x = (int) (sgn8(*((*p)++)) * xy_scale);
    y = (int) (sgn8(*((*p)++)) * xy_scale);
    break;

  case 12:
    d = *((*p)++) + *((*p)++) * 0x100 + *((*p)++) * 0x10000;
    x = (int) (sgn12(d & 0x00000fff) * xy_scale);
    y = (int) (sgn12(d >> 12) * xy_scale);
    break;
  }

#if _COMPRESS
  fprintf(fp, "%3.3x%3.3x", y & 0xfff, x & 0xfff);
#else
  fprintf(fp, "%d %d ", x, y);
#endif
}

int char_offset(int i, font_hdr *h, char *p)
{
int chunk_off = h->offset[i/32];
int char_off;
int off;

  if (chunk_off == 0)
    return 0;

  char_off = ((int *) (p + chunk_off))[i % 32];
  if (char_off == 0)
    return 0;

  off = chunk_off + char_off;
  if (p[off] & 0x08 == 0)
    return 0;

  return off;
}

char *charname(int i)
{
static char buff[12];

  if (charnames[i] == 0)
  {
    sprintf(buff, "Unknown%d", i);
    return buff;
  }
  else
    return charnames[i];
}

void restore(char *p, char *q, FILE *out)
{
  if (out)
    fclose(out);

  if (q)
    heap_free((void *) q);

  if (p)
    heap_free((void *) p);
}

BOOL convert(char *output, void *input)
{

#define terminate {restore(p, q, out); return FALSE;}

FILE *out = NULL;
os_filestr f;
char *fontname;

font_hdr    h;
metrics_hdr m;

char *p = NULL;
char *q = NULL;
short *widths;

char *ptr;
char *mark;
char data;

int i;
int o;

  f.action   = 17;                         /* read size of Outlines file */
  f.name     = (char *) input;
  if (wimpt_complain(os_file(&f)))
    terminate;
  if (f.action != 1)
  {
    werr(FALSE, "Outlines file not found");
    terminate;
  }

  p = (char *) heap_alloc(f.start);        /* allocate memory for Outlines file */
  if (p == NULL)
  {
    werr(FALSE, "Unable to allocate memory");
    terminate;
  }

  f.action   = 255;                        /* read Outlines file into memory */
  f.loadaddr = (int) p;
  f.execaddr = 0;
  if (wimpt_complain(os_file(&f)))
    terminate;

  h = *((font_hdr *) p);                   /* read outlines header into struct h */

  ptr = p + sizeof(font_hdr);              /* read font name */
  fontname = ptr + *((short *) ptr);

  if (h.bpp != 0)
    terminate;

  for (i = 0; i < 8; i++)                  /* mark empty chunks with offset = 0 */
    if (h.offset[i] == h.offset[i+1])
      h.offset[i] = 0;

  xy_scale = 1000 / h.dsize;               /* scale outlines to 1000 unit design size */
  h.x0 = (int) (h.x0 * xy_scale);
  h.y0 = (int) (h.y0 * xy_scale);
  h.xd = (int) (h.xd * xy_scale);
  h.yd = (int) (h.yd * xy_scale);



  sprintf(buff, "%s.IntMetrics", fontname);

  f.action = 15;                           /* read size of IntMetrics file */
  f.name = buff;
  f.start = (int) "Font$Path";
  if (wimpt_complain(os_file(&f)))
    terminate;
  if (f.action != 1)
  {
    werr(FALSE, "IntMetrics file not found");
    terminate;
  }

  q = (char *) heap_alloc(f.start);        /* allocate memory for IntMetrics file */
  if (q == NULL)
  {
    werr(FALSE, "Unable to allocate memory");
    terminate;
  }

  f.action = 14;                           /* read IntMetrics file into memory */
  f.loadaddr = (int) q;
  f.execaddr = 0;
  f.start = (int) "Font$Path";
  if (wimpt_complain(os_file(&f)))
    terminate;

  m = *((metrics_hdr *) q);                /* read metrics header into struct m */
  widths = (short *) (q + sizeof(metrics_hdr) + (8 * m.chars));

  out = fopen(output, "w");
  if (!out)
  {
    werr(FALSE, "Unable to open output file");
    terminate;
  }


  fprintf(out, "FDDict begin\n");             /* write file header */
  fprintf(out, "/DF 10 dict def\n\n");
  fprintf(out, "DF begin\n\n");

  fprintf(out, "/FontType 3 def\n");
  fprintf(out, "/FontMatrix[.001 0 0 .001 0 0]def\n");
  fprintf(out, "/FontBBox[%d %d %d %d]def\n\n", h.x0, h.y0, h.x0+h.xd, h.y0+h.yd);


  fprintf(out, "/Encoding[\n");               /* write encoding vector */
  for (i = 0; i < 256; i++)
  {
    o = char_offset(i, &h, p);

    if (o == 0)
      fprintf(out, " /.notdef");
    else
      fprintf(out, " /%s", charname(i));

    if ((i % 4) == 3)
      fprintf(out, "\n");
  }
  fprintf(out, "]def\n\n");


  fprintf(out, "/CharDefs 256 dict def\n");  /* write character definitions */
  fprintf(out, "CharDefs begin\n");

#if _COMPRESS
  fprintf(out, "/.notdef<00000000>def\n");
#else
  fprintf(out, "/.notdef{0 0 M}def\n");
#endif

  for (i = 0; i < 256; i++)
  {
    o = char_offset(i, &h, p);
    if (o == 0)
      continue;

    ptr = p + o + 1;

#if _COMPRESS
    fprintf(out, "/%s<", charname(i));
#else
    fprintf(out, "/%s{", charname(i));
#endif

    xy_bits = (p[o] & 0x01) ? 12 : 8;      /* select 8 or 12 bit definitions */

    ptr += (xy_bits * 4)/8;                /* skip bounding box */

    mark = ptr + 1;

    do
    {
      switch (data = *(ptr++), data & 0x03)
      {
      case 0:
        switch (data & 0x0c)
        {
        case 0x04:                         /* skip over skeleton lines */
          while (data = *(ptr++), data);
          break;

        case 0x08:                         /* composite character inclusions */
          while (data = *(ptr++), data)
          {
#if _COMPRESS
            fprintf(out, "03%2.2x", data);
            fprintxy(out, &ptr);
#else
            fprintf(out, "/%s ", charname(data));
            fprintxy(out, &ptr);
            fprintf(out, "I ");
#endif
          }
          break;
        }
        break;

      case 1:                              /* move */
#if _COMPRESS
        fprintf(out, "00");
        fprintxy(out, &ptr);
#else
        fprintxy(out, &ptr);
        fprintf(out, "M ");
#endif
        break;

      case 2:                              /* line */
#if _COMPRESS
        fprintf(out, "01");
        fprintxy(out, &ptr);
#else
        fprintxy(out, &ptr);
        fprintf(out, "L ");
#endif
        break;

      case 3:                              /* curve */
#if _COMPRESS
        fprintf(out, "02");
        fprintxy(out, &ptr);
        fprintxy(out, &ptr);
        fprintxy(out, &ptr);
#else
        fprintxy(out, &ptr);
        fprintxy(out, &ptr);
        fprintxy(out, &ptr);
        fprintf(out, "B ");
#endif
        break;
      }
    } while (data & 0x0f);

    if (ptr == mark)                       /* ensure at least one path segment */
#if _COMPRESS
      fprintf(out, "00000000");
#else
      fprintf(out, "0 0 M");
#endif

#if _COMPRESS
    fprintf(out, ">def\n");
#else
    fprintf(out, "}bind def\n");
#endif
  }

  fprintf(out, "end\n\n");


  fprintf(out, "/CharWidths 256 dict def\n"); /* write font metrics */
  fprintf(out, "CharWidths begin\n");

  fprintf(out, "/.notdef 0 def\n");

  for (i = 0; i < 256; i++)
    if (char_offset(i, &h, p))
      fprintf(out, "/%s %d def\n", charname(i), widths[m.mapping[i]]);

  fprintf(out, "end\n\n");


  fprintf(out, "/BuildChar /BC load def\n");  /* write file footer */
  fprintf(out, "end\n\n");
  fprintf(out, "fonts /%s dup DF definefont put\n", fontname);
  fprintf(out, "end\n\n");


  fclose(out);                             /* tidy up */
  sprintf(buff, "SetType %s ff5", output);
  os_cli(buff);

  heap_free((void *) q);
  heap_free((void *) p);

  return TRUE;
}

void infobox(void)
{
dbox  d;

  if ((d = dbox_new("ProgInfo")) != NULL)
  {
    dbox_show(d);
    dbox_fillin(d);
    dbox_dispose(&d);
  }
}

void icon_menuproc(void *handle, char *hit)
{
  UNUSED(handle);

  switch (hit[0])
  {
  case icon_menu_INFO:
    infobox();
    break;

  case icon_menu_QUIT:
    exit(0);
  }
}

void iconbar_handler(wimp_eventstr *e, void *handle)
{
  UNUSED(handle);

  if ((e->e != wimp_ESEND) && (e->e != wimp_ESENDWANTACK))
    return;

  if (e->data.msg.hdr.action != wimp_MDATALOAD)
    return;

  if (e->data.msg.data.dataload.type != 0xff6)
    return;

  strncpy(buff, e->data.msg.data.dataload.name, 212);

  saveas(0xff5, "FontData", 0, convert, 0, 0, (void *) buff);
}

void read_encoding(void)
{
FILE *enc = res_openfile("Encoding", "r");
static char name[129];
char c;
int i, j;

  for (i = 0; i < 256; i++)
  {
    j = 0;                           /* read next name */
    do
    {
      if (feof(enc))
        werr(TRUE, "EOF in Encoding file");

      if (j > 128)
        werr(TRUE, "Name too long in Encoding file");

      c = fgetc(enc);
      c = (c > 32) ? c : 0;
      name[j++] = c;

    } while (c);

    if (strcmp(name, ".notdef"))
    {
    char *p = (char *) malloc(j);

      if (p == NULL)
        werr(TRUE, "Insufficient memory for encoding vector");

      strcpy(p, name);
      charnames[i] = p;
    }
  }

  fclose(enc);
}

void initialise(void)
{
  wimpt_init("FontRead");
  res_init("FontRead");

  template_init();
  dbox_init();

  flex_init();
  heap_init(TRUE);

  read_encoding();

  if ((icon_menu = menu_new("FontRead", ">Info, Quit")) == NULL)
    exit(1);

  baricon_handle = baricon("!fontread", 1, null_proc);
  if (!event_attachmenu(win_ICONBAR, icon_menu, icon_menuproc, 0))
    exit(1);

  win_register_event_handler(win_ICONBARLOAD, iconbar_handler, 0);
}

/****************************** MAIN PROGRAM *******************************/

int main()
{
  initialise();

  while (TRUE)
    event_process();

  return 0;
}
