Handle<Value>
DynamicPngStack::PngEncodeAsync(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 1)
        return VException("One argument required - callback function.");

    if (!args[0]->IsFunction())
        return VException("First argument must be a function.");

    Local<Function> callback = Local<Function>::Cast(args[0]);
    DynamicPngStack *png = ObjectWrap::Unwrap<DynamicPngStack>(args.This());

    encode_request *enc_req = (encode_request *)malloc(sizeof(*enc_req));
    if (!enc_req)
        return VException("malloc in DynamicPngStack::PngEncodeAsync failed.");

    enc_req->callback = Persistent<Function>::New(callback);
    enc_req->png_obj = png;
    enc_req->png = NULL;
    enc_req->png_len = 0;
    enc_req->error = NULL;

    eio_custom(EIO_PngEncode, EIO_PRI_DEFAULT, EIO_PngEncodeAfter, enc_req);

    ev_ref(EV_DEFAULT_UC);
    png->Ref();

    return Undefined();
}
Handle<Value>
DynamicPngStack::PngEncodeAsync(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 1)
        return VException("One argument required - callback function.");

    if (!args[0]->IsFunction())
        return VException("First argument must be a function.");

    Local<Function> callback = Local<Function>::Cast(args[0]);
    DynamicPngStack *png = ObjectWrap::Unwrap<DynamicPngStack>(args.This());

    encode_request *enc_req = (encode_request *)malloc(sizeof(*enc_req));
    if (!enc_req)
        return VException("malloc in DynamicPngStack::PngEncodeAsync failed.");

    enc_req->callback = Persistent<Function>::New(callback);
    enc_req->png_obj = png;
    enc_req->png = NULL;
    enc_req->png_len = 0;
    enc_req->error = NULL;


    uv_work_t* req = new uv_work_t;
    req->data = enc_req;
    uv_queue_work(uv_default_loop(), req, UV_PngEncode, (uv_after_work_cb)UV_PngEncodeAfter);

    png->Ref();

    return Undefined();
}
int
DynamicPngStack::EIO_PngEncode(eio_req *req)
{
    encode_request *enc_req = (encode_request *)req->data;
    DynamicPngStack *png = (DynamicPngStack *)enc_req->png_obj;

    std::pair<Point, Point> optimal = png->optimal_dimension();
    Point top = optimal.first, bot = optimal.second;

    // printf("width, height: %d, %d\n", bot.x - top.x, bot.y - top.y);

    png->offset = top;
    png->width = bot.x - top.x;
    png->height = bot.y - top.y;

    unsigned char *data = (unsigned char*)malloc(sizeof(*data) * png->width * png->height * 4);
    if (!data) {
        enc_req->error = strdup("malloc failed in DynamicPngStack::EIO_PngEncode.");
        return 0;
    }
    memset(data, 0xFF, png->width*png->height*4);

    png->construct_png_data(data, top);

    buffer_type pbt = (png->buf_type == BUF_BGR || png->buf_type == BUF_BGRA) ?
        BUF_BGRA : BUF_RGBA;

    try {
        PngEncoder encoder(data, png->width, png->height, pbt);
        encoder.encode();
        free(data);
        enc_req->png_len = encoder.get_png_len();
        enc_req->png = (char *)malloc(sizeof(*enc_req->png)*enc_req->png_len);
        if (!enc_req->png) {
            enc_req->error = strdup("malloc in DynamicPngStack::EIO_PngEncode failed.");
            return 0;
        }
        else {
            memcpy(enc_req->png, encoder.get_png(), enc_req->png_len);
        }
    }
    catch (const char *err) {
        enc_req->error = strdup(err);
    }

    return 0;
}
int 
DynamicPngStack::EIO_PngEncodeAfter(eio_req *req)
{
    HandleScope scope;

    ev_unref(EV_DEFAULT_UC);
    encode_request *enc_req = (encode_request *)req->data;
    DynamicPngStack *png = (DynamicPngStack *)enc_req->png_obj;

    Handle<Value> argv[3];

    if (enc_req->error) {
        argv[0] = Undefined();
        argv[1] = Undefined();
        argv[2] = ErrorException(enc_req->error);
    }
    else {
        Buffer *buf = Buffer::New(enc_req->png_len);
        memcpy(BufferData(buf), enc_req->png, enc_req->png_len);
        argv[0] = buf->handle_;
        argv[1] = png->Dimensions();
        argv[2] = Undefined();
    }

    TryCatch try_catch; // don't quite see the necessity of this

    enc_req->callback->Call(Context::GetCurrent()->Global(), 3, argv);

    if (try_catch.HasCaught())
        FatalException(try_catch);

    enc_req->callback.Dispose();
    free(enc_req->png);
    free(enc_req->error);

    png->Unref();
    free(enc_req);

    return 0;
}