/*
 *   The real io-stuff is in tarkin-io.c
 *   (this one has to be rewritten to write ogg streams ...)
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "mem.h"
#include "tarkin.h"
#include "yuv.h"


#define N_FRAMES 1



TarkinStream *
tarkin_stream_new ()
{
  TarkinStream *s = (TarkinStream *) CALLOC (1, sizeof (TarkinStream));

  if (!s)
    return NULL;
  memset (s, 0, sizeof (*s));

  s->frames_per_buf = N_FRAMES;

  return s;
}


void
tarkin_stream_destroy (TarkinStream * s)
{
  uint32_t i, j;

  if (!s)
    return;

  for (i = 0; i < s->n_layers; i++) {
    if (s->layer[i].waveletbuf) {
      for (j = 0; j < s->layer[i].n_comp; j++) {
        wavelet_3d_buf_destroy (s->layer[i].waveletbuf[j]);
        FREE (s->layer[i].packet[j].data);
      }
      FREE (s->layer[i].waveletbuf);
      FREE (s->layer[i].packet);
    }
  }

  if (s->layer)
    FREE (s->layer);

  if (s->headers.header)
    FREE (s->headers.header);

  if (s->headers.header1)
    FREE (s->headers.header1);

  if (s->headers.header2)
    FREE (s->headers.header2);


  FREE (s);
}


int
tarkin_analysis_init (TarkinStream * s, TarkinInfo * ti,
    TarkinError (*free_frame) (void *s, void *ptr),
    TarkinError (*packet_out) (void *s, ogg_packet * ptr), void *user_ptr)
{
  if ((!ti->inter.numerator) || (!ti->inter.denominator))
    return (-TARKIN_FAULT);
  if ((!free_frame) || (!packet_out))
    return (-TARKIN_FAULT);
  s->ti = ti;
  s->free_frame = free_frame;
  s->packet_out = packet_out;
  s->user_ptr = user_ptr;
  return (0);
}


extern int
tarkin_analysis_add_layer (TarkinStream * s, TarkinVideoLayerDesc * tvld)
{
  int i;
  TarkinVideoLayer *layer;

  if (s->n_layers) {
    s->layer = REALLOC (s->layer, (s->n_layers + 1) * sizeof (*s->layer));
  } else {
    s->layer = MALLOC (sizeof (*s->layer));
  }
  layer = s->layer + s->n_layers;
  memset (layer, 0, sizeof (*s->layer));
  memcpy (&layer->desc, tvld, sizeof (TarkinVideoLayerDesc));

  s->n_layers++;
  s->ti->n_layers = s->n_layers;
  s->ti->layer = s->layer;

  switch (layer->desc.format) {
    case TARKIN_GRAYSCALE:
      layer->n_comp = 1;
      layer->color_fwd_xform = grayscale_to_y;
      layer->color_inv_xform = y_to_grayscale;
      break;
    case TARKIN_RGB24:
      layer->n_comp = 3;
      layer->color_fwd_xform = rgb24_to_yuv;
      layer->color_inv_xform = yuv_to_rgb24;
      break;
    case TARKIN_RGB32:
      layer->n_comp = 3;
      layer->color_fwd_xform = rgb32_to_yuv;
      layer->color_inv_xform = yuv_to_rgb32;
      break;
    case TARKIN_RGBA:
      layer->n_comp = 4;
      layer->color_fwd_xform = rgba_to_yuv;
      layer->color_inv_xform = yuv_to_rgba;
      break;
    default:
      return -TARKIN_INVALID_COLOR_FORMAT;
  };

#ifdef DBG_OGG
  printf ("dbg_ogg:add_layer %d with %d components\n",
      s->n_layers, layer->n_comp);
#endif

  layer->waveletbuf = (Wavelet3DBuf **) CALLOC (layer->n_comp,
      sizeof (Wavelet3DBuf *));

  layer->packet = MALLOC (layer->n_comp * sizeof (*layer->packet));
  memset (layer->packet, 0, layer->n_comp * sizeof (*layer->packet));

  for (i = 0; i < layer->n_comp; i++) {
    layer->waveletbuf[i] = wavelet_3d_buf_new (layer->desc.width,
        layer->desc.height, layer->desc.frames_per_buf);
    layer->packet[i].data = MALLOC (layer->desc.bitstream_len);
    layer->packet[i].storage = layer->desc.bitstream_len;
  }
  /*
     max_bitstream_len += layer->desc.bitstream_len
     + 2 * 10 * sizeof(uint32_t) * layer->n_comp;    
   */
  return (TARKIN_OK);
}

TarkinError
_analysis_packetout (TarkinStream * s, uint32_t layer_id, uint32_t comp)
{
  ogg_packet op;
  oggpack_buffer opb;
  uint8_t *data;
  uint32_t data_len;
  int i;

  data = s->layer[layer_id].packet[comp].data;
  data_len = s->layer[layer_id].packet[comp].data_len;

  oggpack_writeinit (&opb);
  oggpack_write (&opb, 0, 8);   /* No feature flags for now */
  oggpack_write (&opb, layer_id, 12);
  oggpack_write (&opb, comp, 12);
  for (i = 0; i < data_len; i++)
    oggpack_write (&opb, *(data + i), 8);

  op.b_o_s = 0;
  op.e_o_s = data_len ? 0 : 1;
  op.granulepos = 0;
  op.bytes = oggpack_bytes (&opb) + 4;
  op.packet = opb.buffer;
#ifdef DBG_OGG
  printf ("dbg_ogg: writing packet layer %d, comp %d, data_len %d %s\n",
      layer_id, comp, data_len, op.e_o_s ? "eos" : "");
#endif
  s->layer[layer_id].packet[comp].data_len = 0; /* so direct call => eos */
  return (s->packet_out (s, &op));
}

void
_stream_flush (TarkinStream * s)
{
  uint32_t i, j;

  s->current_frame_in_buf = 0;

  for (i = 0; i < s->n_layers; i++) {
    TarkinVideoLayer *layer = &s->layer[i];

    for (j = 0; j < layer->n_comp; j++) {
      uint32_t comp_bitstream_len;
      TarkinPacket *packet = layer->packet + j;

        /**
         *  implicit 6:1:1 subsampling
         */
      if (j == 0)
        comp_bitstream_len =
            6 * layer->desc.bitstream_len / (layer->n_comp + 5);
      else
        comp_bitstream_len = layer->desc.bitstream_len / (layer->n_comp + 5);

      if (packet->storage < comp_bitstream_len) {
        packet->storage = comp_bitstream_len;
        packet->data = REALLOC (packet->data, comp_bitstream_len);
      }

      wavelet_3d_buf_dump ("color-%d-%03d.pgm",
          s->current_frame, j, layer->waveletbuf[j], j == 0 ? 0 : 128);

      wavelet_3d_buf_fwd_xform (layer->waveletbuf[j],
          layer->desc.a_moments, layer->desc.s_moments);

      wavelet_3d_buf_dump ("coeff-%d-%03d.pgm",
          s->current_frame, j, layer->waveletbuf[j], 128);

      packet->data_len = wavelet_3d_buf_encode_coeff (layer->waveletbuf[j],
          packet->data, comp_bitstream_len);

      _analysis_packetout (s, i, j);
    }
  }
}


uint32_t
tarkin_analysis_framein (TarkinStream * s, uint8_t * frame,
    uint32_t layer_id, TarkinTime * date)
{
  TarkinVideoLayer *layer;

  if (!frame)
    return (_analysis_packetout (s, 0, 0));     /* eos */
  if ((layer_id >= s->n_layers) || (date->denominator == 0))
    return (TARKIN_FAULT);

  layer = s->layer + layer_id;
  layer->color_fwd_xform (frame, layer->waveletbuf, s->current_frame_in_buf);
  /* We don't use this feature for now, neither date... */
  s->free_frame (s, frame);

  s->current_frame_in_buf++;

  if (s->current_frame_in_buf == s->frames_per_buf)
    _stream_flush (s);

#ifdef DBG_OGG
  printf ("dbg_ogg: framein at pos %d/%d, n� %d,%d on layer %d\n",
      date->numerator, date->denominator,
      layer->frameno, s->current_frame, layer_id);
#endif

  layer->frameno++;
  return (++s->current_frame);
}




/**
 *   tarkin_stream_read_header() is now info.c:_tarkin_unpack_layer_desc()
 */



TarkinError
tarkin_stream_get_layer_desc (TarkinStream * s,
    uint32_t layer_id, TarkinVideoLayerDesc * desc)
{
  if (layer_id > s->n_layers - 1)
    return -TARKIN_INVALID_LAYER;

  memcpy (desc, &(s->layer[layer_id].desc), sizeof (TarkinVideoLayerDesc));

  return TARKIN_OK;
}

TarkinError
tarkin_synthesis_init (TarkinStream * s, TarkinInfo * ti)
{
  s->ti = ti;
  s->layer = ti->layer;         /* It was malloc()ed by headerin() */
  s->n_layers = ti->n_layers;
  return (TARKIN_OK);
}

TarkinError
tarkin_synthesis_packetin (TarkinStream * s, ogg_packet * op)
{
  uint32_t i, layer_id, comp, data_len;
  uint32_t flags, junk;
  int nread;
  oggpack_buffer opb;
  TarkinPacket *packet;

#ifdef DBG_OGG
  printf ("dbg_ogg: Reading packet n� %lld, granulepos %lld, len %ld, %s%s\n",
      op->packetno, op->granulepos, op->bytes,
      op->b_o_s ? "b_o_s" : "", op->e_o_s ? "e_o_s" : "");
#endif
  oggpack_readinit (&opb, op->packet, op->bytes);
  flags = oggpack_read (&opb, 8);
  layer_id = oggpack_read (&opb, 12);   /* Theses are  required for */
  comp = oggpack_read (&opb, 12);       /* data hole handling (or maybe
                                         * packetno would be enough ?) */
  nread = 4;

  if (flags) {                  /* This is void "infinite future features" feature ;) */
    if (flags & 1 << 7) {
      junk = flags;
      while (junk & 1 << 7)
        junk = oggpack_read (&opb, 8);  /* allow for many future flags
                                           that must be correctly ordonned. */
    }
    /* This shows how to get a feature's data:
       if (flags & TARKIN_FLAGS_EXAMPLE){
       tp->example = oggpack_read(&opb,32);
       junk = tp->example & 3<<30;
       tp->example &= 0x4fffffff;
       }
     */
    for (junk = 1 << 31; junk & 1 << 31;)       /* and many future data */
      while ((junk = oggpack_read (&opb, 32)) & 1 << 30);
    /* That is, feature data comes in 30 bit chunks. We also have
     * 31 potentially usefull bits in last chunk. */
  }

  nread = (opb.ptr - opb.buffer);
  data_len = op->bytes - nread;

#ifdef DBG_OGG
  printf ("   layer_id %d, comp %d, meta-data %dB, w3d data %dB.\n",
      layer_id, comp, nread, data_len);
#endif

  /* We now have for shure our data. */
  packet = &s->layer[layer_id].packet[comp];
  if (packet->data_len)
    return (-TARKIN_UNUSED);    /* Previous data wasn't used */

  if (packet->storage < data_len) {
    packet->storage = data_len + 255;
    packet->data = REALLOC (packet->data, packet->storage);
  }

  for (i = 0; i < data_len; i++)
    packet->data[i] = oggpack_read (&opb, 8);

  packet->data_len = data_len;

  return (TARKIN_OK);
}

TarkinError
tarkin_synthesis_frameout (TarkinStream * s,
    uint8_t ** frame, uint32_t layer_id, TarkinTime * date)
{
  int j;
  TarkinVideoLayer *layer = &s->layer[layer_id];

  if (s->current_frame_in_buf == 0) {
    *frame = MALLOC (layer->desc.width * layer->desc.height * layer->n_comp);
    for (j = 0; j < layer->n_comp; j++) {
      TarkinPacket *packet = layer->packet + j;

      if (packet->data_len == 0)
        goto err_out;

      wavelet_3d_buf_decode_coeff (layer->waveletbuf[j], packet->data,
          packet->data_len);

      wavelet_3d_buf_dump ("rcoeff-%d-%03d.pgm",
          s->current_frame, j, layer->waveletbuf[j], 128);

      wavelet_3d_buf_inv_xform (layer->waveletbuf[j],
          layer->desc.a_moments, layer->desc.s_moments);

      wavelet_3d_buf_dump ("rcolor-%d-%03d.pgm",
          s->current_frame, j, layer->waveletbuf[j], j == 0 ? 0 : 128);
    }

    /* We did successfylly read a block from this layer, acknowledge it. */
    for (j = 0; j < layer->n_comp; j++)
      layer->packet[j].data_len = 0;
  }

  layer->color_inv_xform (layer->waveletbuf, *frame, s->current_frame_in_buf);
  s->current_frame_in_buf++;
  s->current_frame++;

  if (s->current_frame_in_buf == s->frames_per_buf)
    s->current_frame_in_buf = 0;

  date->numerator = layer->frameno * s->ti->inter.numerator;
  date->denominator = s->ti->inter.denominator;
#ifdef DBG_OGG
  printf ("dbg_ogg: outputting frame pos %d/%d from layer %d.\n",
      date->numerator, date->denominator, layer_id);
#endif
  layer->frameno++;
  return (TARKIN_OK);
err_out:
  FREE (*frame);
  return (TARKIN_NEED_MORE);
}

int
tarkin_synthesis_freeframe (TarkinStream * s, uint8_t * frame)
{
  FREE (frame);

  return (TARKIN_OK);
}