#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ttf.h"
#include "font.h"
#include "error.h"
#include "basic.h"
#include "loadtabl.h"
#include "ttf2pk.h"

#define SEEK_SET 0
#define max(a,b) ((a)>(b)? (a):(b))

/*
   This module is very long. It just reads the data file according to the
   True Type Font Manual. Many parts of the file are unused code at the 
   moment.
*/

typedef struct _dir_chuck {
  char tag[4];
  ULONG cksum;
  ULONG off;
  ULONG len;
} dir_chuck;


int Font_load_MAXP(Font *f,FILE *fp,int off,int len);
int Font_load_HEAD(Font *f,FILE *fp,int off,int len);
int Font_load_CMAP(Font *f,FILE *fp,int off,int len);
int Font_load_HHEA(Font *f,FILE *fp,int off,int len);
int Font_load_HMTX(Font *f,FILE *fp,int off,int len);
int Font_load_LOCA(Font *f,FILE *fp,int off,int len);
int Font_load_NAME(Font *f,FILE *fp,int off,int len);
int Font_load_FPGM(Font *f,FILE *fp,int off,int len);
int Font_load_CVT(Font *f,FILE *fp,int off,int len);
int Font_load_HDMX(Font *f,FILE *fp,int off,int len);
int Font_load_KERN(Font *f,FILE *fp,int off,int len);
int Font_load_LTSH(Font *f,FILE *fp,int off,int len);
int Font_load_PREP(Font *f,FILE *fp,int off,int len);
int Font_load_PCLT(Font *f,FILE *fp,int off,int len);


Font *Font_Init(char *name)
{
  FILE *fp;
  Font *f;
  int ntabs,i;
  char *tag;
  dir_chuck *ch;

  if ((fp = fopen(name,READ_BIN))==NULL)
  {
    Error_throw(error_invalid_filename,name);
    return NULL;
  }
  if ((f = TTFALLOC(Font))==NULL)
    return NULL;
  f->name = strdup(name);
  f->fp = fp;
  f->version = get_Fixed(fp);
  ntabs = get_USHORT(fp);
  /* skip search optimize data, I don't use it now */
  fseek(fp,12L,SEEK_SET);

  /* load ntabs tables pointer */
  ch = (dir_chuck *) malloc(sizeof(dir_chuck)*ntabs);
  for(i=0;i<ntabs;i++)
  {
    ch[i].tag[0] = get_BYTE(fp);
    ch[i].tag[1] = get_BYTE(fp);
    ch[i].tag[2] = get_BYTE(fp);
    ch[i].tag[3] = get_BYTE(fp);
    ch[i].cksum = get_ULONG(fp);
    ch[i].off = get_ULONG(fp);
    ch[i].len = get_ULONG(fp);
  }

  /* 
     load MAXP & HEAD tables first because they will be used by other
     tables.
  */

  for(i=0;i<ntabs;i++)
  {
    tag = ch[i].tag;
    if (*tag == 'm' && strncmp(tag,"maxp",4)==0)
    {
      if (Font_load_MAXP(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag =='h' && strncmp(tag,"head",4)==0)
    {
      if (Font_load_HEAD(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
  }  
  /* allocate outline data from MAXP table */
  f->glyf = TTFALLOC(OUTLINE);
  f->glyf->endPtsOfContours = (USHORT *) 
    malloc(sizeof(USHORT)*max(f->profile->maxContours,f->profile->maxCompositeContours));
  f->glyf->xCoordinates = (LONG *) 
    malloc(sizeof(LONG)*max(f->profile->maxPoints,f->profile->maxCompositePoints));
  f->glyf->yCoordinates = (LONG *) 
    malloc(sizeof(LONG)*max(f->profile->maxPoints,f->profile->maxCompositePoints));
  f->glyf->flags = (BYTE *) 
    malloc(sizeof(BYTE)*max(f->profile->maxPoints,f->profile->maxCompositePoints));

  /* 
     read the other tables now except the GLYF table will. It will be read
     on demand.
  */

  for(i=0;i<ntabs;i++)
  {
    tag = ch[i].tag;
    if (*tag == 'c' && !strncmp(tag,"cmap",4))
    {
      if (Font_load_CMAP(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'g' && !strncmp(tag,"glyf",4))
    {
      f->glyfoff = ch[i].off;
    }
    else if (*tag == 'h' && !strncmp(tag,"head",4))
    {
    }
    else if (*tag == 'h' && !strncmp(tag,"hhea",4))
    {
      if (Font_load_HHEA(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'h' && !strncmp(tag,"hmtx",4))
    {
      if (Font_load_HMTX(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'l' && !strncmp(tag,"loca",4))
    {
      if (Font_load_LOCA(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'm' && !strncmp(tag,"maxp",4))
    {
    }
    else if (*tag == 'n' && !strncmp(tag,"name",4))
    {
      if (Font_load_NAME(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'p' && !strncmp(tag,"post",4))
    {
    /*
      if (Font_load_POST(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    */
    }
    else if (*tag == 'f' && !strncmp(tag,"fpgm",4))
    {
      if (Font_load_FPGM(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'O' && !strncmp(tag,"OS/2",4))
    {
    /*
      if (Font_load_OS2(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    */
    }
    else if (*tag == 'c' && !strncmp(tag,"cvt",3))
    {
      if (Font_load_CVT(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'h' && !strncmp(tag,"hdmx",4))
    {
      if (Font_load_HDMX(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'k' && !strncmp(tag,"kern",4))
    {
      if (Font_load_KERN(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'L' && !strncmp(tag,"LTSH",4))
    {
      if (Font_load_LTSH(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'p' && !strncmp(tag,"prep",4))
    {
      if (Font_load_PREP(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else if (*tag == 'W' && !strncmp(tag,"WIN",3))
    {
    /*
      if (Font_load_WIN(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    */
    }
    else if (*tag == 'V' && !strncmp(tag,"VDMX",4))
    {
    /*
      if (Font_load_VDMX(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    */
    }
    else if (*tag == 'F' && !strncmp(tag,"FOCA",4))
    {
    /*
      if (Font_load_FOCA(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    */
    }
    else if (*tag == 'P' && !strncmp(tag,"PCLT",4))
    {
      if (Font_load_PCLT(f,fp,ch[i].off,ch[i].len))
        goto return_error;
    }
    else
    {
      Error_throw(error_invalid_table,tag);
    }
  }
  return f;

return_error:
  if (f)
    free(f);
  return NULL;
}

    
int Font_load_CMAP(Font *f,FILE *fp,int off,int len)
{
  CMAP0 *m0;
  CMAP2 *m2;
  int i,j,length,num_var;
  int version;
  ULONG *offs;

  fseek(fp,off,SEEK_SET);
  /* ignore version */
  version = get_USHORT(fp);
  f->numberOfEncoding = get_USHORT(fp);
  f->cmap = (CMAP *) malloc(sizeof(CMAP)*f->numberOfEncoding);
  offs = (ULONG *) malloc(sizeof(ULONG)*f->numberOfEncoding);
  for(i=0;i<f->numberOfEncoding;i++)
  {
    f->cmap[i].PlateformID = get_USHORT(fp);
    f->cmap[i].EncodingID = get_USHORT(fp);
    offs[i] = get_ULONG(fp);
  }
  for(i=0;i<f->numberOfEncoding;i++)
  {
    fseek(fp,off+offs[i],SEEK_SET);
    f->cmap[i].format = get_USHORT(fp);
    switch(f->cmap[i].format)
    {
    case 0:
      m0 = f->cmap[i].map.cmap0 = TTFALLOC(CMAP0);
      fread(m0->glyphIdArray,1,256,fp);
      break;
    case 2:
      m2 = f->cmap[i].map.cmap2 = TTFALLOC(CMAP2);
      num_var = 0;
      length = get_USHORT(fp);
      get_USHORT(fp);
      for(j=0;j<256;j++)
      {
        m2->subHeaderKeys[j] = get_USHORT(fp)/8;
        if (num_var < m2->subHeaderKeys[j])
          num_var = m2->subHeaderKeys[j];
      }
      length -= (256+3)*2+num_var*8;
      /* ignore version */
      m2->data = (USHORT *) malloc(sizeof(USHORT)*num_var*8);
      if (m2->data == NULL)
      {
        Error_throw(error_out_of_memory);
        free(m2);
        free(f->cmap);
        f->cmap = NULL;
        return 1;
      }
      for(j=0;j<=num_var;j++)
      {
        m2->data[j*4] = get_USHORT(fp);
        m2->data[j*4+1] = get_USHORT(fp);
        m2->data[j*4+2] = get_USHORT(fp);
        m2->data[j*4+3] = get_USHORT(fp)-(num_var-j)*8-2;
      }
      m2->glyphIdArray = (USHORT *) malloc(length);
      if (m2->glyphIdArray == NULL)
      {
        Error_throw(error_out_of_memory);
        free(m2->data);
        free(m2);
        free(f->cmap);
        f->cmap = NULL;
        return 1;
      }
      length /=2;
      for(j=0;j<length;j++)
        m2->glyphIdArray[j] = get_USHORT(fp);
#if 0
      for(j=0;j<256;j++)
      {
        if (m2->subHeaderKeys[j] != 0)
        {
          k = m2->subHeaderKeys[j];
          printf("%dth Offset %d firstcode %x entry %d idoff %d iddrange %d\n",j,k,m2->data[k*4],m2->data[k*4+1],m2->data[k*4+2],m2->data[k*4+3]);
        }
      }
      for(j=0;j<length;j++)
      {
        if (j&&(j%16==0))
        {
          printf("\n");
        }
        printf("%3d ",m2->glyphIdArray[j]);
      }
#endif
      break;
    case 4:
    case 6:
      f->numberOfEncoding--;
      Error_throw(error_not_implement);
      break;
    default:
      Error_throw(error_invalid_format);
    }
  }
  free(offs);
  return 0;
}

int Font_load_GLYF(Font *f,FILE *fp,int off,int len)
{
  OUTLINE *ol;
  int totpoint,i;
  BYTE b,fl,c,st;

  fseek(fp,f->glyfoff+off,SEEK_SET);
  ol = f->glyf;
  ol->valid = 0;
  ol->numberOfContours = get_SHORT(fp);
  if ((USHORT)(ol->numberOfContours) > f->profile->maxContours)
  {
    Error_throw(error_invalid_table);
    return 0;
  }
  ol->xMin = get_FWord(fp);
  ol->yMin = get_FWord(fp);
  ol->xMax = get_FWord(fp);
  ol->yMax = get_FWord(fp);
  if (ol->numberOfContours > 0)
  {
    for(i = 0;i < ol->numberOfContours;i++)
      ol->endPtsOfContours[i] = get_USHORT(fp);
    totpoint = ol->endPtsOfContours[ol->numberOfContours-1]+1;
    ol->instructionLength = get_USHORT(fp);
    if (ol->instructionLength)
      fread(ol->instruction,1,ol->instructionLength,fp);
    for(i=0;i<totpoint;)
    {
      ol->flags[i++] = c = get_BYTE(fp);
      if (c& FLAG_REPEAT)
      {
        st = get_BYTE(fp);
        while(st--) ol->flags[i++] = c;
      }
    }

    for(i=0;i<totpoint;i++)
    {
      fl = ol->flags[i];
      if (fl & FLAG_X_SHORT_VECTOR)
      {
        b = get_BYTE(fp);
        if (fl & FLAG_X_SAME)
          ol->xCoordinates[i] = b;
        else
          ol->xCoordinates[i] = -b;
      }
      else if (fl & FLAG_X_SAME)
        ol->xCoordinates[i] = 0;
      else
        ol->xCoordinates[i] = get_SHORT(fp);
    }
    for(i=0;i<totpoint;i++)
    {
      fl = ol->flags[i];
      if (fl & FLAG_Y_SHORT_VECTOR)
      {
        b = get_BYTE(fp);
        if (fl & FLAG_Y_SAME)
          ol->yCoordinates[i] = b;
        else
          ol->yCoordinates[i] = -b;
      }
      else if (fl & FLAG_Y_SAME)
        ol->yCoordinates[i] = 0;
      else
        ol->yCoordinates[i] = get_SHORT(fp);
    }
  }
  else
  {
    /* composite fonts not implemented yet */
    Error_throw(error_not_implement);
    return 0;
  }
  for(i=1;i<totpoint;i++)
  {
    ol->xCoordinates[i] +=ol->xCoordinates[i-1];
    ol->yCoordinates[i] +=ol->yCoordinates[i-1];
  }
  f->glyf->valid = 1;
  return 1;
}
    
int Font_load_HEAD(Font *f,FILE *fp,int off,int len)
{
  Fixed version;
  HEAD *h;

  fseek(fp,off,SEEK_SET);
  version = get_Fixed(fp);
  if (version != 0x00010000)
  {
    Error_throw(error_invalid_version);
    return 1;
  }
  f->head = h = TTFALLOC(HEAD);
  if (h == NULL)
    return 1;

  h->fontRevision = get_Fixed(fp);
  /* forget checksum and magicnumber */
  get_ULONG(fp);get_ULONG(fp);
  h->flags = get_USHORT(fp);
  h->unitsPerEm = get_USHORT(fp);
  /* forget date data */
  get_ULONG(fp);get_ULONG(fp);get_ULONG(fp);get_ULONG(fp);
  
  h->xMin = get_FWord(fp);
  h->yMin = get_FWord(fp);
  h->xMax = get_FWord(fp);
  h->yMax = get_FWord(fp);
  h->macStyle = get_USHORT(fp);
  h->lowestRecPPEM = get_USHORT(fp);
  h->fontDirectionHint = get_SHORT(fp);
  h->indexToLocFormat = get_SHORT(fp);
  h->glyphDataFormat = get_SHORT(fp);

  return 0;
}

int Font_load_HHEA(Font *f,FILE *fp,int off,int len)
{
  int version;
  HORIZON *h;

  fseek(fp,off,SEEK_SET);
  version = get_Fixed(fp);
  if (version != 0x00010000)
  {
    Error_throw(error_invalid_version);
    return 1;
  }
  
  f->horz = h = TTFALLOC(HORIZON);
  if (h == NULL)
    return 1;

  h->ascender = get_FWord(fp);
  h->decender = get_FWord(fp);
  h->lineGap  = get_FWord(fp);
  h->advanceWidthMax = get_uFWord(fp);
  h->minLeftSideBearing = get_FWord(fp);
  h->minRightSideBearing= get_FWord(fp);
  h->xMaxExtent = get_FWord(fp);
  h->caretSlopeRise = get_SHORT(fp);
  h->caretSlopeRun  = get_SHORT(fp);
  /* stub bytes */
  get_SHORT(fp);get_SHORT(fp);get_SHORT(fp);get_SHORT(fp);
  h->metricDataFormat = get_SHORT(fp);
  h->numberOfHMetrics = get_USHORT(fp);
  h->longHorMetrics = NULL;
  h->leftSideBearing = NULL;
  return 0;
}

int Font_load_HMTX(Font *f,FILE *fp,int off,int len)
{
  int i;
  HORIZON *h;

  h = f->horz;

  fseek(fp,off,SEEK_SET);
  h->longHorMetrics = 
    (HorMetric *) malloc(sizeof(HorMetric)*h->numberOfHMetrics);
  len = f->profile->numglyphs - h->numberOfHMetrics;
  h->leftSideBearing = (FWord *) malloc(sizeof(SHORT)*len);
  for(i=0;i<h->numberOfHMetrics;i++)
  {
    h->longHorMetrics[i].advanceWidth = get_uFWord(fp);
    h->longHorMetrics[i].lsb = get_FWord(fp);
  }
  for(i=0;i<len;i++)
    h->leftSideBearing[i] = get_FWord(fp);

  return 0;
}

int Font_load_LOCA(Font *f,FILE *fp,int off,int len)
{
  int i;

  fseek(fp,off,SEEK_SET);
  if (f->head == NULL)
  {
    Error_throw(error_need_head_table);
    return 1;
  }
  if (f->profile == NULL)
  {
    Error_throw(error_need_maxp_table);
    return 1;
  }
  f->indexToLocation = (ULONG *) malloc(sizeof(ULONG)*(f->profile->numglyphs+1));
  if (f->head->indexToLocFormat == 0)
    for(i=0;i<=f->profile->numglyphs;i++)
      f->indexToLocation[i] = get_USHORT(fp)*2;
  else
    for(i=0;i<=f->profile->numglyphs;i++)
      f->indexToLocation[i] = get_ULONG(fp);
  return 0;
}

int Font_load_MAXP(Font *f,FILE *fp,int off,int len)
{
  int version;
  PROFILE *p;

  fseek(fp,off,SEEK_SET);
  version = get_Fixed(fp);
  if (version != 0x00010000)
  {
    Error_throw(error_invalid_version);
    return 1;
  }
  
  p = f->profile = TTFALLOC(PROFILE);

  if ( p == NULL)
    return 1;
  p-> numglyphs = get_USHORT(fp);
  p-> maxPoints = get_USHORT(fp);
  p-> maxContours = get_USHORT(fp);
  p-> maxCompositePoints = get_USHORT(fp);
  p-> maxCompositeContours = get_USHORT(fp);
  p-> maxZones = get_USHORT(fp);
  p-> maxTwilightPoints = get_USHORT(fp);
  p-> maxStorage = get_USHORT(fp);
  p-> maxFunctionDefs = get_USHORT(fp);
  p-> maxInstructionDefs = get_USHORT(fp);
  p-> maxStackElements = get_USHORT(fp);
  p-> maxSizeOfInstructions = get_USHORT(fp);
  p-> maxComponentElements = get_USHORT(fp);
  p-> maxComponentDepth = get_USHORT(fp);
  return 0;
}

int Font_load_NAME(Font *f,FILE *fp,int off,int len)
{
  return 0;
}

int Font_load_CVT(Font *f,FILE *fp,int off,int len)
{
  int i;

  fseek(fp,off,SEEK_SET);
  f->cvt = (USHORT *) malloc(sizeof(USHORT)*len/2);
  f->cvtlen = len/2;
  for(i=0;i<len/2;i++)
    f->cvt[i] = get_USHORT(fp);
  return 0;
}

int Font_load_FPGM(Font *f,FILE *fp,int off,int len)
{
  int i;

  fseek(fp,off,SEEK_SET);
  f->fpgm = (BYTE *) malloc(sizeof(BYTE)*len);
  f->fpgmlen = len;
  for(i=0;i<len;i++)
    f->fpgm[i] = get_BYTE(fp);
  return 0;
}

int Font_load_KERN(Font *f,FILE *fp,int off,int len)
{
  return 0;
}

int Font_load_LTSH(Font *f,FILE *fp,int off,int len)
{
  return 0;
}

int Font_load_PREP(Font *f,FILE *fp,int off,int len)
{
  int rlen;

  fseek(fp,off,SEEK_SET);
  f->prep = (BYTE *) malloc(len);
  rlen = fread(f->prep,1,len,fp);
  if (rlen != len)
  {
    Error_throw(error_invalid_prep);
    return 1;
  }
  f->preplen = len;
  return 0;
}

int Font_load_HDMX(Font *f,FILE *fp,int off,int len)
{
  return 0;
}

int Font_load_PCLT(Font *f,FILE *fp,int off,int len)
{
  return 0;
}
