#include "hypervisor.h"
#include "workers/connect.h"
#include "workers/disconnect.h"
#include "workers/startByName.h"
#include "workers/shutdownByName.h"
#include "workers/deleteByName.h"
#include "workers/getAllDomains.h"
#include "workers/getDomainByName.h"

Nan::Persistent<v8::Function> Hypervisor::constructor;

Hypervisor::Hypervisor() {
}

Hypervisor::~Hypervisor() {
}

void Hypervisor::Init(v8::Local<v8::Object> exports) {
  Nan::HandleScope scope;

  // Prepare constructor template
  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Hypervisor").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  Nan::SetPrototypeMethod(tpl, "connect", Connect);
  Nan::SetPrototypeMethod(tpl, "disconnect", Disconnect);
  Nan::SetPrototypeMethod(tpl, "startByName", StartByName);
  Nan::SetPrototypeMethod(tpl, "shutdownByName", ShutdownByName);
  Nan::SetPrototypeMethod(tpl, "deleteByName", DeleteByName);

  Nan::SetPrototypeMethod(tpl, "getAllDomains", GetAllDomains);
  Nan::SetPrototypeMethod(tpl, "getDomainByName", GetDomainByName);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Hypervisor").ToLocalChecked(), tpl->GetFunction());
}

void Hypervisor::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    Hypervisor* hyperv = new Hypervisor();
    hyperv->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    v8::Local<v8::Value> argv[] = { info[0] };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(Nan::NewInstance(cons, 1, argv).ToLocalChecked());
  }
}

void Hypervisor::Connect(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[1]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new ConnectWorker(&(obj->_handler), *v8::String::Utf8Value(args[0]->ToString()), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::Disconnect(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[0]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new DisconnectWorker(&(obj->_handler), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::StartByName(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[1]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new StartByNameWorker(&(obj->_handler), *v8::String::Utf8Value(args[0]->ToString()), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::ShutdownByName(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[1]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new ShutdownByNameWorker(&(obj->_handler), *v8::String::Utf8Value(args[0]->ToString()), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::DeleteByName(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[1]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new DeleteByNameWorker(&(obj->_handler), *v8::String::Utf8Value(args[0]->ToString()), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::GetAllDomains(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[0]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new GetAllDomainsWorker(&(obj->_handler), callback));

  args.GetReturnValue().Set(Nan::Null());
}

void Hypervisor::GetDomainByName(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Hypervisor* obj = ObjectWrap::Unwrap<Hypervisor>(args.Holder());

  v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[1]);
  Nan::Callback* callback = new Nan::Callback(cb);
  Nan::AsyncQueueWorker(new GetDomainByNameWorker(&(obj->_handler), *v8::String::Utf8Value(args[0]->ToString()), callback));

  args.GetReturnValue().Set(Nan::Null());
}