//
//  lzfse.c
//  
//
//  Created by lzsak on 2018/6/17.
//

#include <node.h>
#include <nan.h>
#include "lzfse.h"

namespace lzfse {

using v8::Context;
using v8::ArrayBuffer;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

static inline void *lzfse_reallocf(void *x, size_t s)
{
  void *y = realloc(x, s);
  if (y == 0)
  {
    free(x);
    return 0;
  }
  return y;
}

void Compress(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  void *aux = malloc(lzfse_encode_scratch_size());
  if(aux == 0){
    isolate->ThrowException(v8::Exception::TypeError(
        String::NewFromUtf8(isolate, "aux is death")));
    return;
  }
  Local<v8::Context> context = isolate->GetCurrentContext();
  Local<v8::Object> obj = args[0]->ToObject(context).ToLocalChecked();
  Local<v8::Value> lengthObj = obj->Get(context, v8::String::NewFromUtf8(isolate, "length")).ToLocalChecked();
  Local<v8::Uint32> length = lengthObj->ToUint32(context).ToLocalChecked();
  uint8_t *in = (uint8_t *)node::Buffer::Data(args[0]);
  size_t in_size = length->Value();
  size_t out_size = in_size;
  uint8_t *out = (uint8_t *)malloc(out_size);
  size_t code_size = lzfse_encode_buffer(out, out_size, in, in_size, aux);
  args.GetReturnValue().Set(node::Buffer::New(isolate, (char*)out, code_size).ToLocalChecked());
}
void Decompress(const FunctionCallbackInfo<Value>& args){
   Isolate *isolate = args.GetIsolate();
   void *aux = malloc(lzfse_decode_scratch_size());
   if (aux == 0)
   {
     isolate->ThrowException(v8::Exception::TypeError(
         String::NewFromUtf8(isolate, "aux is death")));
     return;
   }
   Local<v8::Context> context = isolate->GetCurrentContext();
   Local<v8::Object> obj = args[0]->ToObject(context).ToLocalChecked();
   Local<v8::Value> lengthObj = obj->Get(context, v8::String::NewFromUtf8(isolate, "length")).ToLocalChecked();
   Local<v8::Uint32> length = lengthObj->ToUint32(context).ToLocalChecked();
   uint8_t *in = (uint8_t *)node::Buffer::Data(args[0]);
   size_t in_size = length->Value();
   size_t out_size = in_size * 4;
   uint8_t *out = (uint8_t *)malloc(out_size);
   size_t code_size = lzfse_decode_buffer(out, out_size, in, in_size, aux);
   if(code_size != out_size){
     out = (uint8_t *)lzfse_reallocf((void *)out, code_size);
   }
   args.GetReturnValue().Set(node::Buffer::New(isolate, (char *)out, code_size).ToLocalChecked());
}

void init(Local<Object> exports) {
  NODE_SET_METHOD(exports, "compress", Compress);
  NODE_SET_METHOD(exports, "decompress", Decompress);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, init)

}  // namespace demo