#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
//
#include "proto.h"
#include "flash.h"
#include "main.h"
#include "bucket.h"
#include "bitcount.h"
#include "matrix.h"
#include "rectangle.h"
#include "bbox.h"
#include "gradient.h"


static S32 fillstyle_write(FILLSTYLE *style);
static S32 linestyle_write(LINESTYLE *style);
static S32 fillstylearray_write(FILLSTYLEARRAY *styles);
static S32 linestylearray_write(LINESTYLEARRAY *styles);
static S32 calculate_bbox(SHAPE *shape);


S32 shape_add_fillstyle(SHAPE *shp, FILLSTYLE *style) {

  FILLSTYLEARRAY *array;

  array = &shp->fillstyles;
  if (!array->styles) {
    array->styles = malloc(sizeof(FILLSTYLE));
    if (!array->styles)                 return 1;
    array->n = 0;
  } else {
    FILLSTYLE *newstyles;
    newstyles = realloc(array->styles, (array->n+1)*sizeof(FILLSTYLE));
    if (!newstyles)                     return 1;
    array->styles = newstyles;
  }

  memcpy(array->styles+array->n, style, sizeof(FILLSTYLE));
  array->n++;

  return 0;
}


S32 shape_add_linestyle(SHAPE *shp, LINESTYLE *style) {

  LINESTYLEARRAY *array;

  array = &shp->linestyles;
  if (!array->styles) {
    array->styles = malloc(sizeof(LINESTYLE));
    if (!array->styles)                 return 1;
    array->n = 0;
  } else {
    LINESTYLE *newstyles;
    newstyles = realloc(array->styles, (array->n+1)*sizeof(LINESTYLE));
    if (!newstyles)                     return 1;
    array->styles = newstyles;
  }

  memcpy(array->styles+array->n, style, sizeof(LINESTYLE));
  array->n++;

  return 0;
}


S32 shape_add_record(SHAPE *shape, SHAPERECORD *rec) {

  if (!shape->records) {
    shape->records = malloc(sizeof(SHAPERECORD));
    if (!shape->records)                return 1;
    shape->n = 0;
  } else {
    SHAPERECORD *newrecords;
    newrecords = realloc(shape->records, (shape->n+1)*sizeof(SHAPERECORD));
    if (!newrecords)                    return 1;
    shape->records = newrecords;
  }

  memcpy(shape->records+shape->n, rec, sizeof(SHAPERECORD));
  shape->n++;

  return 0;
}


S32 shape_create(SHAPE **shp) {

  SHAPE *shape;

  shape = malloc(sizeof(SHAPE));
  if (!shape)                           return 1;

  *shp = shape;
  memset(shape, 0, sizeof(SHAPE));

  return 0;
}


S32 shape_write(SHAPE *shape) {

  S32 i;
  U32 fillbits, linebits, ptr;

  if (calculate_bbox(shape))                                return 1;

  if (flush_bucket())                                       return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                                      return 1;
  if (write_ushort(shape->id))                              return 1;

  if (rect_write(&shape->bbox))                             return 1;
  if (fillstylearray_write(&shape->fillstyles))             return 1;
  if (linestylearray_write(&shape->linestyles))             return 1;
  fillbits = bitcount(shape->fillstyles.n, 0, 0, 0);
  linebits = bitcount(shape->linestyles.n, 0, 0, 0);
  if (write_ubits(4, fillbits))                             return 1;
  if (write_ubits(4, linebits))                             return 1;

  for (i = 0; i < shape->n; i++) {
    SHAPERECORD *rec;
    rec = shape->records+i;

    switch (rec->type) {
    case SHAPERECORD_END:
      if (write_ubits(1, 0))                                return 1;
      if (write_ubits(5, 0))                                return 1;
      break;
    case SHAPERECORD_STYLE:
      {
        if (write_ubits(1, 0))                              return 1;
        if (write_ubits(5, rec->flags))                     return 1;
        if (rec->flags & SHAPERECORD_STYLE_MOVE) {
          U32 bits;
          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;
        }
        if (rec->flags & SHAPERECORD_STYLE_FILL0)
          if (write_ubits(fillbits, rec->fillstyle0))       return 1;
        if (rec->flags & SHAPERECORD_STYLE_FILL1)
          if (write_ubits(fillbits, rec->fillstyle1))       return 1;
        if (rec->flags & SHAPERECORD_STYLE_LINE)
          if (write_ubits(linebits, rec->linestyle))        return 1;
      }
      break;
    case SHAPERECORD_STRAIGHT:
      {
        U32 bits;

        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 SHAPERECORD_CURVE:
      {
        U32 bits;

        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;
    }
  }

  return update_record_header(stagDefineShape3, ptr);
}



S32 fillstylearray_write(FILLSTYLEARRAY *array) {

  S32 i;

  if (array->n > 254) {
    if (write_ubyte(255))                                   return 1;
    if (write_ushort(array->n))                             return 1;
  } else
    if (write_ubyte(array->n))                              return 1;

  for (i = 0; i < array->n; i++)
    if (fillstyle_write(array->styles+i))                   return 1;

  return 0;
}


S32 linestylearray_write(LINESTYLEARRAY *array) {

  S32 i;

  if (array->n > 254) {
    if (write_ubyte(255))                                   return 1;
    if (write_ushort(array->n))                             return 1;
  } else
    if (write_ubyte(array->n))                              return 1;

  for (i = 0; i < array->n; i++)
    if (linestyle_write(array->styles+i))                   return 1;

  return 0;
}


S32 fillstyle_write(FILLSTYLE *style) {

  if (write_ubyte(style->type))                             return 1;
  switch (style->type) {
  case FILLSTYLE_SOLID:
    if (write_ubyte( style->fill.solid      & 0xff))        return 1;
    if (write_ubyte((style->fill.solid>> 8) & 0xff))        return 1;
    if (write_ubyte((style->fill.solid>>16) & 0xff))        return 1;
    if (write_ubyte((style->fill.solid>>24) & 0xff))        return 1;
    break;
  case FILLSTYLE_LINEAR:
  case FILLSTYLE_RADIAL:
    if (matrix_write(&style->fill.gradient.matrix))         return 1;
    if (gradient_write(&style->fill.gradient.gradient))     return 1;
    break;
  case FILLSTYLE_TILED:
  case FILLSTYLE_CLIPPED:
    if (write_ushort(style->fill.bitmap.id))                return 1;
    if (matrix_write(&style->fill.bitmap.matrix))           return 1;
    break;
  }
  return 0;
}


S32 linestyle_write(LINESTYLE *style) {

  if (write_ushort(style->width))                           return 1;
  if (write_ubyte( style->rgba      & 0xff))                return 1;
  if (write_ubyte((style->rgba>> 8) & 0xff))                return 1;
  if (write_ubyte((style->rgba>>16) & 0xff))                return 1;
  if (write_ubyte((style->rgba>>24) & 0xff))                return 1;

  return 0;
}


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

S32 calculate_bbox(SHAPE *shape) {

  U16 width;
  S32 i, context;
  S32 x, y;

  width = 20;

  bbox_init(&shape->bbox, &context);

  x = y = 0;
  for (i = 0; i < shape->n; i++) {
    SHAPERECORD *rec;
    rec = shape->records + i;
    switch (rec->type) {
    case SHAPERECORD_STYLE:
      if (rec->flags == SHAPERECORD_STYLE_MOVE) {
        x += rec->x;
        y += rec->y;
        bbox_add_point(&shape->bbox, &context, x, y, width);
      }
      break;
    case SHAPERECORD_CURVE:
      x += rec->x;
      y += rec->y;
      bbox_add_point(&shape->bbox, &context, x, y, width);
      x += rec->ctrlx;
      y += rec->ctrly;
      bbox_add_point(&shape->bbox, &context, x, y, width);
      break;
    case SHAPERECORD_STRAIGHT:
      x += rec->x;
      y += rec->y;
      bbox_add_point(&shape->bbox, &context, x, y, width);
      break;
    case SHAPERECORD_STYLE_LINE:
      width = 20+shape->linestyles.styles[rec->linestyle].width;
      break;
    }
  }

  return 0;
}
