#include "CSocketPair.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

using namespace v8;

Persistent<Function> CSocketPair::constructor;

void CSocketPair::Init(Handle<Object> exports) {
    Isolate* isolate = Isolate::GetCurrent();

    // Prepare constructor template
    Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
    tpl->SetClassName(String::NewFromUtf8(isolate, "SocketPair"));
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    // Property
    tpl->InstanceTemplate()->SetAccessor(
            String::NewFromUtf8(isolate, "fd0"), GetFd0);
    tpl->InstanceTemplate()->SetAccessor(
            String::NewFromUtf8(isolate, "fd1"), GetFd1);

    // Prototype
    NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close);

    constructor.Reset(isolate, tpl->GetFunction());
    exports->Set(String::NewFromUtf8(isolate, "SocketPair"),
            tpl->GetFunction());
}

void CSocketPair::New(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    if (args.IsConstructCall()) {
        // Invoked as constructor: `new MyObject(...)`
        CSocketPair* obj = new CSocketPair();
        obj->Wrap(args.This());
        args.GetReturnValue().Set(args.This());
    } else {
        // Invoked as plain function `MyObject(...)`, turn into construct call.
        const int argc = 1;
        Local<Value> argv[argc] = { args[0] };
        Local<Function> cons = Local<Function>::New(isolate, constructor);
        args.GetReturnValue().Set(cons->NewInstance(argc, argv));
    }
}

void CSocketPair::Close(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);
    CSocketPair* obj = ObjectWrap::Unwrap<CSocketPair>(args.Holder());
    if(obj->fds[0] != -1) {
        ::close(obj->fds[0]);
        obj->fds[0] = -1;
    }
    if(obj->fds[1] != -1) {
        ::close(obj->fds[1]);
        obj->fds[1] = -1;
    }
}

void
CSocketPair::GetFd0(Local<String> property,
        const PropertyCallbackInfo<Value>& info) {
    Isolate* isolate = Isolate::GetCurrent();
    CSocketPair* obj = ObjectWrap::Unwrap<CSocketPair>(info.Holder());
    info.GetReturnValue().Set(Number::New(isolate, obj->fds[0]));
}

void
CSocketPair::GetFd1(Local<String> property,
        const PropertyCallbackInfo<Value>& info) {
    Isolate* isolate = Isolate::GetCurrent();
    CSocketPair* obj = ObjectWrap::Unwrap<CSocketPair>(info.Holder());
    info.GetReturnValue().Set(Number::New(isolate, obj->fds[1]));
}

CSocketPair::CSocketPair() {
    ::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
}

CSocketPair::~CSocketPair() {
}
