#include "cell.h"

#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg.h"

struct datastream {
  plm_t *plm;
  JSValue callback;
  JSContext *js;
  int width;
  int height;
};

typedef struct datastream datastream;

#include "cell.h"

#include "limits.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include "cbuf.h"

void datastream_free(JSRuntime *rt,datastream *ds)
{
  plm_destroy(ds->plm);
  free(ds);
}

QJSCLASS(datastream,)

struct datastream *ds_openvideo(void *raw, size_t rawlen)
{
  struct datastream *ds = malloc(sizeof(*ds));
  void *newraw = malloc(rawlen);
  memcpy(newraw,raw,rawlen);
  ds->plm = plm_create_with_memory(newraw, rawlen, 1);

  if (!ds->plm) {
    free(ds);
    free(newraw);
    return NULL;
  }
    
  return ds;
}

void ds_advance(struct datastream *ds, double s) {
  plm_decode(ds->plm, s);
}

void ds_seek(struct datastream *ds, double time) {
  plm_seek(ds->plm, time, false);
}

double ds_length(struct datastream *ds) {
  return plm_get_duration(ds->plm);
}

JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
JSC_CCALL(datastream_seek, ds_seek(js2datastream(js,self), js2number(js,argv[0])))
JSC_CCALL(datastream_advance, ds_advance(js2datastream(js,self), js2number(js,argv[0])))
JSC_CCALL(datastream_duration, return number2js(js,ds_length(js2datastream(js,self))))
JSC_CCALL(datastream_framerate, return number2js(js,plm_get_framerate(js2datastream(js,self)->plm)))

static const JSCFunctionListEntry js_datastream_funcs[] = {
  MIST_FUNC_DEF(datastream, time, 0),
  MIST_FUNC_DEF(datastream, seek, 1),
  MIST_FUNC_DEF(datastream, advance, 1),
  MIST_FUNC_DEF(datastream, duration, 0),
  MIST_FUNC_DEF(datastream, framerate, 0),
};

static void render_frame(plm_t *mpeg, plm_frame_t *frame, void *d) {
  datastream *ds = d;
  if (JS_IsNull(ds->callback)) return;
  uint8_t *rgb = malloc(frame->height*frame->width*4);
  memset(rgb,255,frame->height*frame->width*4);
  plm_frame_to_rgba(frame, rgb, frame->width*4);
  
  // Create surface data object instead of SDL_Surface
  JS_FRAME(ds->js);
  JS_ROOT(surfData, JS_NewObject(ds->js));
  JS_SetPropertyStr(ds->js, surfData.val, "width", JS_NewInt32(ds->js, frame->width));
  JS_SetPropertyStr(ds->js, surfData.val, "height", JS_NewInt32(ds->js, frame->height));
  JS_SetPropertyStr(ds->js, surfData.val, "format", JS_NewString(ds->js, "rgba32"));
  JS_SetPropertyStr(ds->js, surfData.val, "pitch", JS_NewInt32(ds->js, frame->width*4));
  JS_SetPropertyStr(ds->js, surfData.val, "pixels", js_new_blob_stoned_copy(ds->js, rgb, frame->height*frame->width*4));

  JSValue s[1];
  s[0] = surfData.val;
  JSValue cb = JS_DupValue(ds->js,ds->callback);
  JSValue cret = JS_Call(ds->js, cb, JS_NULL, 1, s);
  JS_FreeValue(ds->js,cb);
  JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame);
  free(rgb);
  // TODO: raise exception
}

JSC_CCALL(os_make_video,
  size_t len;
  void *data = js_get_blob_data(js,&len,argv[0]);
  if (data == -1 || !data) return JS_EXCEPTION;
  datastream *ds = ds_openvideo(data, len);
  if (!ds) return JS_ThrowReferenceError(js, "Video file was not valid.");
  ds->js = js;
  ds->callback = JS_NULL;
  plm_set_video_decode_callback(ds->plm, render_frame, ds);
  return datastream2js(js,ds);  
)

static const JSCFunctionListEntry js_video_funcs[] = {
  MIST_FUNC_DEF(os, make_video, 1),
};

CELL_USE_FUNCS(js_video_funcs)
