#include "sectSprites.h"

#include "../common/bufio.h"
#include "../Utils/Logger.h"

#include <cstdlib>
#include <cstring>

int SIFSpritesSect::GetSpriteCount(const uint8_t *data, int datalen)
{
  const uint8_t *data_end = data + (datalen - 1);
  return read_U16(&data, data_end);
}

bool SIFSpritesSect::Decode(const uint8_t *data, int datalen, SIFSprite *sprites, int *nsprites_out, int maxsprites)
{
  const uint8_t *data_end = data + (datalen - 1);
  int i, f, nsprites;

  nsprites = read_U16(&data, data_end);
  if (nsprites_out)
    *nsprites_out = nsprites;

  if (nsprites >= maxsprites)
  {
    LOG_ERROR("SIFSpritesSect::Decode: too many sprites in file (nsprites={}, maxsprites={})", nsprites, maxsprites);
    return 1;
  }

  LOG_DEBUG("SIFSpritesSect: loading {} sprites", nsprites);
  for (i = 0; i < nsprites; i++)
  {
    if (data > data_end)
    {
      LOG_ERROR("SIFSpritesSect::Decode: section corrupt: overran end of data");
      return 1;
    }

    // read sprite-level fields
    sprites[i].w           = read_U8(&data, data_end);
    sprites[i].h           = read_U8(&data, data_end);
    sprites[i].spritesheet = read_U8(&data, data_end);

    sprites[i].nframes = read_U8(&data, data_end);
    sprites[i].ndirs   = read_U8(&data, data_end);

    if (sprites[i].ndirs > SIF_MAX_DIRS)
    {
      LOG_ERROR("SIFSpritesSect::Decode: SIF_MAX_DIRS exceeded on sprite {} (ndirs={})", i, sprites[i].ndirs);
      return 1;
    }


    for (f = 0; f < sprites[i].ndirs; f++)
      LoadRect(&sprites[i].bbox[f], &data, data_end);
    LoadRect(&sprites[i].solidbox, &data, data_end);

    LoadPoint(&sprites[i].spawn_point, &data, data_end);

    LoadPointList(&sprites[i].block_l, &data, data_end);
    LoadPointList(&sprites[i].block_r, &data, data_end);
    LoadPointList(&sprites[i].block_u, &data, data_end);
    LoadPointList(&sprites[i].block_d, &data, data_end);

    // malloc enough space to hold the specified number
    // of apple fritters, i mean, frames.
    sprites[i].frame = (SIFFrame *)malloc(sizeof(SIFFrame) * sprites[i].nframes);

    // then load all frames
    for (f = 0; f < sprites[i].nframes; f++)
    {
      if (LoadFrame(&sprites[i].frame[f], sprites[i].ndirs, &data, data_end))
        return 1;
    }
  }

  return 0;
}

bool SIFSpritesSect::LoadFrame(SIFFrame *frame, int ndirs, const uint8_t **data, const uint8_t *data_end)
{
  // sets defaults for un-specified/default fields
  memset(frame, 0, sizeof(SIFFrame));

  for (int d = 0; d < ndirs; d++)
  {
    SIFDir *dir = &frame->dir[d];
    LoadPoint(&dir->sheet_offset, data, data_end);

    int t;
    for (;;)
    {
      t = read_U8(data, data_end);
      if (t == S_DIR_END)
        break;

      switch (t)
      {
        case S_DIR_DRAW_POINT:
          LoadPoint(&dir->drawpoint, data, data_end);
          break;
        case S_DIR_ACTION_POINT:
          LoadPoint(&dir->actionpoint, data, data_end);
          break;
        case S_DIR_ACTION_POINT_2:
          LoadPoint(&dir->actionpoint2, data, data_end);
          break;

        case S_DIR_PF_BBOX:
          LoadRect(&dir->pf_bbox, data, data_end);
          break;

        default:
          LOG_WARN("SIFSpriteSect::LoadFrame: encountered unknown optional field type {}", t);
          return 1;
      }
    }
  }

  return 0;
}

/*
void c------------------------------() {}
*/

void SIFSpritesSect::LoadRect(SIFRect *rect, const uint8_t **data, const uint8_t *data_end)
{
  rect->x1 = (int16_t)read_U16(data, data_end);
  rect->y1 = (int16_t)read_U16(data, data_end);
  rect->x2 = (int16_t)read_U16(data, data_end);
  rect->y2 = (int16_t)read_U16(data, data_end);
}

void SIFSpritesSect::LoadPoint(SIFPoint *pt, const uint8_t **data, const uint8_t *data_end)
{
  pt->x = (int16_t)read_U16(data, data_end);
  pt->y = (int16_t)read_U16(data, data_end);
}

void SIFSpritesSect::LoadPointList(SIFPointList *lst, const uint8_t **data, const uint8_t *data_end)
{
  lst->count = read_U8(data, data_end);
  if (lst->count > SIF_MAX_BLOCK_POINTS)
  {
    LOG_ERROR("SIFSpritesSect::LoadPointList: too many block points ({}, max={})", lst->count, SIF_MAX_BLOCK_POINTS);
    return;
  }

  for (int i = 0; i < lst->count; i++)
  {
    lst->point[i].x = (int16_t)read_U16(data, data_end);
    lst->point[i].y = (int16_t)read_U16(data, data_end);
  }
}
