#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//
#include "proto.h"
#include "bucket.h"
#include "bitcount.h"
#include "matrix.h"
#include "rectangle.h"
#include "main.h"
#include "bbox.h"
#include "flash.h"
#include "parser.h"
#include "fonttext.h"
#include "preprocess.h"

#define MAXFONTS              64


static S32 fontcount = 0;
static FONT *fontlist[MAXFONTS+1];

static S32 glyph_index(FONT *font, U8 letter, S32 *write);
static S32 is_glyph_used(FONT *font, U8 glyph);


S32 add_glyph_shape(GLYPH *glyph, GLYPHSHAPE *shape) {
// add a shape to a glyph
  if (!glyph->shapes) {
    glyph->shapes = malloc(sizeof(GLYPHSHAPE));
    if (!glyph->shapes) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    glyph->shapecount = 0;

  } else {
    GLYPHSHAPE *newshapes;
    newshapes = realloc(glyph->shapes, (glyph->shapecount+1)*sizeof(GLYPHSHAPE));
    if (!newshapes) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    glyph->shapes = newshapes;
  }

  memcpy(glyph->shapes + glyph->shapecount, shape, sizeof(GLYPHSHAPE));
  glyph->shapecount++;

  return 0;
}


S32 add_glyph(FONT *font, GLYPH *glyph) {
// add a glyph to a font
  if (font->glyphcount == 255) {
    fprintf(stderr, "Too many glyphs\n");
    return 1;
  }

  if (!font->glyphs) {
    font->glyphs = malloc(sizeof(GLYPH));
    if (!font->glyphs) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    font->glyphcount = 0;

  } else {
    GLYPH *newglyphs;
    newglyphs = realloc(font->glyphs, (font->glyphcount+1)*sizeof(GLYPH));
    if (!newglyphs) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    font->glyphs = newglyphs;
  }

  memcpy(font->glyphs + font->glyphcount, glyph, sizeof(GLYPH));
  font->glyphcount++;

  return 0;
}


S32 add_text_record(TEXT *text, TEXTREC *rec) {
// add a text-record (text or style-info) to a text definition
  if (!text->records) {
    text->records = malloc(sizeof(TEXTREC));
    if (!text->records) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    text->recordcount = 0;

  } else {
    TEXTREC *newrecords;
    newrecords = realloc(text->records, (text->recordcount+1)*sizeof(TEXTREC));
    if (!newrecords) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    text->records = newrecords;
  }

  memcpy(text->records + text->recordcount, rec, sizeof(TEXTREC));
  text->recordcount++;

  return 0;
}



S32 fonttext_write_fontinfo(FONT *font) {
// write a font-definition
  U32 i, usedglyphs, gi;
  S32 allglyphs;

  if (!font->fontname[0])               return 0;
  if (font->glyphcount == 0)            return 1;

  if (find_macro("INCLUDE_ALL_GLYPHS") >= 0)
    allglyphs = 1;
  else
    allglyphs = 0;

  // count no. of glyphs to write (== no. of used glyphs)
  if (allglyphs)
    usedglyphs = font->glyphcount;
  else {
    usedglyphs = 0;
    for (gi = 0; gi < font->glyphcount; gi++)
      if (is_glyph_used(font, gi))  usedglyphs++;
    if (usedglyphs == 0)                          return 0;
  }

  if (flush_bucket())                             return 1;
  if (write_tag(stagDefineFontInfo, 4+strlen(font->fontname)+usedglyphs))
                                                  return 1;
  // id
  if (write_ushort(font->id))                     return 1;
  // name len
  if (write_ubyte(strlen(font->fontname)))        return 1;
  // name
  for (i = 0; font->fontname[i]; i++)
    if (write_ubyte(font->fontname[i]))           return 1;
  // flags
  if (write_ubyte(8))                             return 1;
  // map
  for (gi = 0; gi < font->glyphcount; gi++)
    if ((is_glyph_used(font, gi)) || (allglyphs))
      if (write_ubyte(font->glyphs[gi].letter))   return 1;

  return 0;
}


S32 fonttext_write_font(FONT *font) {
// write a font-definition
  U32 ptr, i, glyphoffsets, usedglyphs, gi;
  U8 *buffer;
  U16 *offsets;
  S32 allglyphs;

  if (font->glyphcount == 0) {
    fprintf(stderr, "Empty font\n");
    return 1;
  }

  if (find_macro("INCLUDE_ALL_GLYPHS") >= 0)
    allglyphs = 1;
  else
    allglyphs = 0;

  // count no. of glyphs to write (== no. of used glyphs)
  if (allglyphs)
    usedglyphs = font->glyphcount;
  else {
    usedglyphs = 0;
    for (gi = 0; gi < font->glyphcount; gi++)
      if (is_glyph_used(font, gi))  usedglyphs++;
    if (usedglyphs == 0)                          return 0;
  }

  if (flush_bucket())                             return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                            return 1;

  if (write_ushort(font->id))                     return 1;

  if (flush_bucket())                             return 1;

  // write empty glyph-offset-table
  glyphoffsets = read_position(&buffer);
  for (i = 0; i < usedglyphs; i++)
    if (write_ushort(0))                          return 1;

  // pointer to glyph-offsets table
  offsets = (U16 *)(buffer+glyphoffsets);

  for (gi = 0; gi < font->glyphcount; gi++) {
    GLYPH *glyph;
    GLYPHSHAPE *shapes;
    U32 n;

    if ((is_glyph_used(font, gi)) || (allglyphs)) {
      // write a glyph

      glyph = font->glyphs + gi;
      shapes = glyph->shapes;

      if (flush_bucket())                         return 1;
      *offsets++ = read_position(NULL) - glyphoffsets;
      // bits used for fill and linestyles
      if (write_ubits(4, 1))                      return 1;
      if (write_ubits(4, 1))                      return 1;
      //select fillstyle1 = 1
      if (write_ubits(1, 0))                      return 1;
      if (write_ubits(5, SHAPERECORD_STYLE_FILL1)) return 1;
      if (write_ubits(1, 1))                      return 1; // fillstyle1

      for (n = 0; n < glyph->shapecount; n++) {
        S32 bits;
        GLYPHSHAPE *rec;

        rec = shapes+n;
        switch (rec->type) {
        case GLYPHSHAPE_MOVE:
          if ((rec->x) || (rec->y)) {
            if (write_ubits(1, 0))                return 1;
            if (write_ubits(5, SHAPERECORD_STYLE_MOVE))  return 1;
            bits = bitcount_signed(rec->x, rec->y, 0, 0);
            if (write_ubits(5, bits))             return 1;
            if (write_bits(bits, rec->x))         return 1;
            if (write_bits(bits, rec->y))         return 1;
          }
          break;
        case GLYPHSHAPE_LINE:
          if ((rec->x) || (rec->y)) {
            if (write_ubits(1, 1))                return 1;
            if (write_ubits(1, 1))                return 1;
            bits = bitcount_signed(rec->x, rec->y, 0, 0);
            if (bits < 2)   bits = 2;
            if (write_ubits(4, bits-2))           return 1;
            if ((rec->x == 0) || (rec->y == 0)) {
              if (write_ubits(1, 0))              return 1;
              if (rec->y == 0) {
                if (write_ubits(1, 0))            return 1;
                if (write_bits(bits, rec->x))     return 1;
              } else {
                if (write_ubits(1, 1))            return 1;
                if (write_bits(bits, rec->y))     return 1;
              }
            } else {
              if (write_ubits(1, 1))              return 1;
              if (write_bits(bits, rec->x))       return 1;
              if (write_bits(bits, rec->y))       return 1;
            }
          }
          break;
        case GLYPHSHAPE_CURVE:
          if (write_ubits(1, 1))                  return 1;
          if (write_ubits(1, 0))                  return 1;
          bits = bitcount_signed(rec->x, rec->y, rec->ctrlx, rec->ctrly);
          if (bits < 2)   bits = 2;
          if (write_ubits(4, bits-2))             return 1;
          if (write_bits(bits, rec->ctrlx))       return 1;
          if (write_bits(bits, rec->ctrly))       return 1;
          if (write_bits(bits, rec->x))           return 1;
          if (write_bits(bits, rec->y))           return 1;
          break;
        }
      }

      if (write_ubits(1, 0))                      return 1;
      if (write_ubits(5, 0))                      return 1;
    }
  }

  return update_record_header(stagDefineFont, ptr);
}


S32 fonttext_write_text(TEXT *text) {

  U32 ptr, gbits, abits, i, allglyphs, maxglyphs;
  S32 maxadv;
  FONT *font;
  U16 fontsize;
  RECT bbox;

  font = NULL;
  fontsize = 0;

  if (find_macro("INCLUDE_ALL_GLYPHS") >= 0)
    allglyphs = 1;
  else
    allglyphs = 0;

  if (flush_bucket())                             return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                            return 1;

  if (write_ushort(text->id))                     return 1;

  // text bbox
  if (string_bbox(text, &bbox, &maxadv))          return 1;
  if (rect_write(&bbox))                          return 1;

  if (matrix_write(&text->matrix))                return 1;

  // calculate how many bits to use for glyph-index and x-advance
//  gbits = 8;
//  abits = 16;
  maxglyphs = 0;
  i = 0;
  do {
    if (fontlist[i]) {
      if (fontlist[i]->glyphcount > maxglyphs)
        maxglyphs = fontlist[i]->glyphcount;
      i++;
    }
  } while (fontlist[i]);
  gbits = bitcount(maxglyphs, 0, 0, 0);
  abits = 1 + bitcount(maxadv, 0, 0, 0);
  if (write_ubyte(gbits))                         return 1;
  if (write_ubyte(abits))                         return 1;

  // write the text-records
  for (i = 0; i < text->recordcount; i++) {
    TEXTREC *rec;

    rec = text->records + i;
    if (rec->flags == TEXTREC_STRING) {
      // write a text-record containing a string
      S32 g;

      if ((font == NULL) || (fontsize == 0)) {
        fprintf(stderr, "No font has been selected, or illegal fontsize\n");
        return 1;
      }

      // length can be max 127
      if (write_ubyte(rec->data.text.length))    return 1;

      for (g = 0; g < rec->data.text.length; g++) {
        S32 gi, adv, write;
        GLYPH *glyph;

        gi = glyph_index(font, rec->data.text.text[g], &write);
        // gi is the glyph index in the font in memory
        // write is the glyph index in the font when written to the file
        if (gi < 0)                               return 1;
        if (allglyphs)  write = gi;
        if (write_ubits(gbits, write))            return 1;
        glyph = font->glyphs + gi;
        adv = ((font->spacing + glyph->bbox.maxx - glyph->bbox.minx) * fontsize )/1024;
        if (write_bits(abits, adv))               return 1;
      }

    } else {
      // write a text-record containing style-info
      U8 flags;

#define STYLEINFO_IS_STYLEINFO          (1<<7)
#define STYLEINFO_X                     (1<<0)
#define STYLEINFO_Y                     (1<<1)
#define STYLEINFO_COLOUR                (1<<2)
#define STYLEINFO_FONT                  (1<<3)
      flags = STYLEINFO_IS_STYLEINFO;
      if (rec->flags & TEXTREC_MOVE) {
        if (rec->data.style.x)  flags |= STYLEINFO_X;
        if (rec->data.style.y)  flags |= STYLEINFO_Y;
      }
      if (rec->flags & TEXTREC_COLOUR)  flags |= STYLEINFO_COLOUR;
      if (rec->flags & TEXTREC_SIZE) {
        flags |= STYLEINFO_FONT;
        fontsize = rec->data.style.size;
      }
      if (rec->flags & TEXTREC_FONT) {
        S32 i;

        flags |= STYLEINFO_FONT;
        font = NULL;
        i = find_font(rec->data.style.font);
        if (i == -1) {
          fprintf(stderr, "Font id %d not found\n", rec->data.style.font);
          return 1;
        }
        font = fontlist[i];
      }
      if (write_ubyte(flags))                     return 1;
      if (flags & STYLEINFO_FONT)
        if (write_ushort(font->id))               return 1;
      if (flags & STYLEINFO_COLOUR)
        if (write_uint(rec->data.style.colour))   return 1;
      if (flags & STYLEINFO_X)
        if (write_short(rec->data.style.x))       return 1;
      if (flags & STYLEINFO_Y)
        if (write_short(rec->data.style.y))       return 1;
      if (flags & STYLEINFO_FONT)
        if (write_ushort(fontsize))               return 1;
    }
  }

  if (write_ubyte(0))                             return 1;

  return update_record_header(stagDefineText2, ptr);
}


S32 string_bbox(TEXT *text, RECT *bbox, S32 *advance) {

  S32 i, fontsize, context;
  FONT *font;
  S32 plotx, ploty;

  font = NULL;
  fontsize = 0;

  *advance = 0;

  bbox_init(bbox, &context);
  plotx = ploty = 0;

  for (i = 0; i < text->recordcount; i++) {
    TEXTREC *rec;

    rec = text->records + i;
    if (rec->flags == TEXTREC_STRING) {
      S32 g;
      for (g = 0; g < rec->data.text.length; g++) {
        S32 gi, adv, write;
        GLYPH *glyph;
        RECT gb;

        gi = glyph_index(font, rec->data.text.text[g], &write);
        if (gi < 0)                               return 1;
        glyph = font->glyphs + gi;
        gb.minx = plotx+glyph->bbox.minx*fontsize/1024;
        gb.miny = ploty+glyph->bbox.miny*fontsize/1024;
        gb.maxx = plotx+glyph->bbox.maxx*fontsize/1024;
        gb.maxy = ploty+glyph->bbox.maxy*fontsize/1024;
        bbox_add_bbox(bbox, &context, &gb);
        // calculate advance value
        adv = ((font->spacing + glyph->bbox.maxx - glyph->bbox.minx) * fontsize )/1024;
        if (adv  > *advance)  *advance = adv;
        if (-adv > *advance)  *advance = -adv;
        plotx += adv;
      }

    } else {
      if (rec->flags & TEXTREC_MOVE) {
        plotx += rec->data.style.x;
        ploty += rec->data.style.y;
      }
      if (rec->flags & TEXTREC_SIZE)   fontsize = rec->data.style.size;
      if (rec->flags & TEXTREC_FONT) {
        U16 id;
        U32 i;
        id = rec->data.style.font;
        i = 0;
        font = NULL;
        do {
          if (!fontlist[i])   return 1;
          if (fontlist[i]->id == id)  font = fontlist[i];
          i++;
        } while (!font);
      }
    }
  }

  return 0;
}


S32 mark_glyphs_as_used(S32 fontindex, U8 *string, S32 n) {

  S32 i, gi;
  FONT *font;

  font = fontlist[fontindex];

  for (i = 0; i < n; i++) {
    gi = glyph_index(font, string[i], NULL);
    if (gi == -1)
      return 1;
    else
      font->usedglyphs[gi>>5] |= 1<<(gi &31);
  }

  return 0;
}


S32 find_font(U16 id) {

  S32 i;

  for (i = 0; i < fontcount; i++)
    if (fontlist[i]->id == id)  return i;

  return -1;
}


S32 add_font(FONT *font) {

  if (fontcount == MAXFONTS) {
    fprintf(stderr, "Too many fonts\n");
    return 1;
  }
  fontlist[fontcount++] = font;
  fontlist[fontcount] = NULL;

  return 0;
}

// ----------------------------------------------------------

S32 glyph_index(FONT *font, U8 letter, S32 *write) {
// convert letter to glyph-index in font
  U32 n;

  if (!font)                  return -1;
  if (write)  *write = 0;

  for (n = 0; n < font->glyphcount; n++) {
    if (font->glyphs[n].letter == letter)   return n;
    if (write)
      if (is_glyph_used(font, n))  *write += 1;
  }

  fprintf(stderr, "Character '%c' not defined in the selected font\n", letter);
  return -1;
}


S32 is_glyph_used(FONT *font, U8 glyph) {

  if (font->usedglyphs[glyph>>5] & (1<<(glyph &31)))  return 1;

  return 0;
}
