// This is a generated file, modify: generate/templates/struct_content.cc.
#include <nan.h>
#include <string.h>
#include <chrono>
#include <thread>

extern "C" {
  #include <git2.h>
 }

#include <iostream>
#include "../include/functions/copy.h"
#include "../include/checkout_options.h"

  #include "../include/diff_file.h"
  #include "../include/strarray.h"
  #include "../include/tree.h"
 
using namespace v8;
using namespace node;
using namespace std;


// generated from struct_content.cc
GitCheckoutOptions::GitCheckoutOptions() {
  git_checkout_options wrappedValue = GIT_CHECKOUT_OPTIONS_INIT;
  this->raw = (git_checkout_options*) malloc(sizeof(git_checkout_options));
  memcpy(this->raw, &wrappedValue, sizeof(git_checkout_options));

  this->ConstructFields();
  this->selfFreeing = true;
}

GitCheckoutOptions::GitCheckoutOptions(git_checkout_options* raw, bool selfFreeing) {
  this->raw = raw;
  this->ConstructFields();
  this->selfFreeing = selfFreeing;
}

GitCheckoutOptions::~GitCheckoutOptions() {
  // This is going to cause memory leaks. We'll have to solve that later
  // TODO: Clean up memory better
  if (this->selfFreeing) {
    free(this->raw);
  }
}

void GitCheckoutOptions::ConstructFields() {
                     
          // Set the static method call and set the payload for this function to be
          // the current instance
          this->raw->notify_cb = (git_checkout_notify_cb)notify_cb_cppCallback;
          this->raw->notify_payload = (void *)this;
          this->notify_cb = new NanCallback();
   
          Local<Value> notify_payload = NanUndefined();
          NanAssignPersistent(this->notify_payload, notify_payload);
   
          // Set the static method call and set the payload for this function to be
          // the current instance
          this->raw->progress_cb = (git_checkout_progress_cb)progress_cb_cppCallback;
          this->raw->progress_payload = (void *)this;
          this->progress_cb = new NanCallback();
   
          Local<Value> progress_payload = NanUndefined();
          NanAssignPersistent(this->progress_payload, progress_payload);
             Local<Object> pathsTemp = GitStrarray::New(
            &this->raw->paths,
            false
          )->ToObject();
          NanAssignPersistent(this->paths, pathsTemp);

             Local<Object> baselineTemp = GitTree::New(
            &this->raw->baseline,
            false
          )->ToObject();
          NanAssignPersistent(this->baseline, baselineTemp);

                }

void GitCheckoutOptions::InitializeComponent(Handle<v8::Object> target) {
  NanScope();

  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(JSNewFunction);

  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->SetClassName(NanNew<String>("CheckoutOptions"));

      tpl->InstanceTemplate()->SetAccessor(NanNew<String>("version"), GetVersion, SetVersion);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("checkoutStrategy"), GetCheckoutStrategy, SetCheckoutStrategy);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("disableFilters"), GetDisableFilters, SetDisableFilters);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("dirMode"), GetDirMode, SetDirMode);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("fileMode"), GetFileMode, SetFileMode);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("fileOpenFlags"), GetFileOpenFlags, SetFileOpenFlags);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("notifyFlags"), GetNotifyFlags, SetNotifyFlags);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("notifyCb"), GetNotifyCb, SetNotifyCb);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("notifyPayload"), GetNotifyPayload, SetNotifyPayload);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("progressCb"), GetProgressCb, SetProgressCb);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("progressPayload"), GetProgressPayload, SetProgressPayload);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("paths"), GetPaths, SetPaths);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("baseline"), GetBaseline, SetBaseline);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("targetDirectory"), GetTargetDirectory, SetTargetDirectory);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("ancestorLabel"), GetAncestorLabel, SetAncestorLabel);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("ourLabel"), GetOurLabel, SetOurLabel);
       tpl->InstanceTemplate()->SetAccessor(NanNew<String>("theirLabel"), GetTheirLabel, SetTheirLabel);
  
  Local<Function> _constructor_template = tpl->GetFunction();
  NanAssignPersistent(constructor_template, _constructor_template);
  target->Set(NanNew<String>("CheckoutOptions"), _constructor_template);
}

NAN_METHOD(GitCheckoutOptions::JSNewFunction) {
  NanScope();
  GitCheckoutOptions* instance;

  if (args.Length() == 0 || !args[0]->IsExternal()) {
    instance = new GitCheckoutOptions();
  }
  else {
    instance = new GitCheckoutOptions(static_cast<git_checkout_options*>(Handle<External>::Cast(args[0])->Value()), args[1]->BooleanValue());
  }

  instance->Wrap(args.This());

  NanReturnValue(args.This());
}

Handle<Value> GitCheckoutOptions::New(void* raw, bool selfFreeing) {
  NanEscapableScope();

  Handle<Value> argv[2] = { NanNew<External>((void *)raw), NanNew<Boolean>(selfFreeing) };
  return NanEscapeScope(NanNew<Function>(GitCheckoutOptions::constructor_template)->NewInstance(2, argv));
}

git_checkout_options *GitCheckoutOptions::GetValue() {
  return this->raw;
}

git_checkout_options **GitCheckoutOptions::GetRefValue() {
  return &this->raw;
}

    NAN_GETTER(GitCheckoutOptions::GetVersion) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->version));
     }

    NAN_SETTER(GitCheckoutOptions::SetVersion) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->version = (unsigned int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetCheckoutStrategy) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->checkout_strategy));
     }

    NAN_SETTER(GitCheckoutOptions::SetCheckoutStrategy) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->checkout_strategy = (unsigned int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetDisableFilters) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->disable_filters));
     }

    NAN_SETTER(GitCheckoutOptions::SetDisableFilters) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->disable_filters = (int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetDirMode) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->dir_mode));
     }

    NAN_SETTER(GitCheckoutOptions::SetDirMode) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->dir_mode = (unsigned int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetFileMode) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->file_mode));
     }

    NAN_SETTER(GitCheckoutOptions::SetFileMode) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->file_mode = (unsigned int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetFileOpenFlags) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->file_open_flags));
     }

    NAN_SETTER(GitCheckoutOptions::SetFileOpenFlags) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->file_open_flags = (int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetNotifyFlags) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(NanNew<Number>(wrapper->GetValue()->notify_flags));
     }

    NAN_SETTER(GitCheckoutOptions::SetNotifyFlags) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

         if (value->IsNumber()) {
          wrapper->GetValue()->notify_flags = (unsigned int) value->Int32Value();
        }
     }

      NAN_GETTER(GitCheckoutOptions::GetNotifyCb) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->notify_cb->GetFunction());

     }

    NAN_SETTER(GitCheckoutOptions::SetNotifyCb) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (value->IsFunction()) {
          wrapper->notify_cb = new NanCallback(value.As<Function>());
        }

     }

      int GitCheckoutOptions::notify_cb_cppCallback (
          git_checkout_notify_t why,           const char * path,           const git_diff_file * baseline,           const git_diff_file * target,           const git_diff_file * workdir,           void * payload        ) {
        NotifyCbBaton* baton = new NotifyCbBaton();

          baton->why = why;
          baton->path = path;
          baton->baseline = baseline;
          baton->target = target;
          baton->workdir = workdir;
          baton->payload = payload;
 
        baton->req.data = baton;
        baton->done = false;

        uv_queue_work(uv_default_loop(), &baton->req, notify_cb_asyncWork, notify_cb_asyncAfter);

        while(!baton->done) {
          this_thread::sleep_for(chrono::milliseconds(1));
        }

 
        return baton->result;
      }

      void GitCheckoutOptions::notify_cb_asyncWork(uv_work_t* req) {
        // We aren't doing any work on a seperate thread, just need to
        // access the main node thread in the async after method.
        // However, this worker method is still needed
      }

      void GitCheckoutOptions::notify_cb_asyncAfter(uv_work_t* req, int status) {
        NanScope();

        NotifyCbBaton* baton = static_cast<NotifyCbBaton*>(req->data);
        GitCheckoutOptions* instance = static_cast<GitCheckoutOptions*>(baton->payload);

        if (instance->notify_cb->IsEmpty()) {
            baton->result = 1; // no results acquired
 
          baton->done = true;
          return;
        }

        Local<Value> argv[6] = {
                NanNew((int)baton->why),
                   NanNew(baton->path),
                  NanNew(GitDiffFile::New(&baton->baseline, false)),
                  NanNew(GitDiffFile::New(&baton->target, false)),
                  NanNew(GitDiffFile::New(&baton->workdir, false)),
                NanNew(instance->notify_payload)
          };

        TryCatch tryCatch;
        Handle<Value> result = instance->notify_cb->Call(6, argv);

        if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) {
          Handle<Value> thenProp = result->ToObject()->Get(NanNew("then"));

          if (thenProp->IsFunction()) {
            // we can be reasonbly certain that the result is a promise
            Local<Object> promise = result->ToObject();

            NanAssignPersistent(baton->promise, promise);

            uv_queue_work(uv_default_loop(), &baton->req, notify_cb_asyncWork, notify_cb_asyncPromisePolling);
            return;
          }
        }

        int resultStatus;

         baton->done = true;
      }

      void GitCheckoutOptions::notify_cb_asyncPromisePolling(uv_work_t* req, int status) {
        NanScope();

        NotifyCbBaton* baton = static_cast<NotifyCbBaton*>(req->data);
        Local<Object> promise = NanNew<Object>(baton->promise);
        NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As<Function>());
        Local<Value> argv[1]; // MSBUILD won't assign an array of length 0
        Local<Boolean> isPending = isPendingFn->Call(0, argv)->ToBoolean();

        if (isPending->Value()) {
          uv_queue_work(uv_default_loop(), &baton->req, notify_cb_asyncWork, notify_cb_asyncPromisePolling);
          return;
        }

        NanCallback* isFulfilledFn = new NanCallback(promise->Get(NanNew("isFulfilled")).As<Function>());
        Local<Boolean> isFulfilled = isFulfilledFn->Call(0, argv)->ToBoolean();

        if (isFulfilled->Value()) {
          NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As<Function>());
          Handle<Value> result = resultFn->Call(0, argv);
          int resultStatus;

           baton->done = true;
        }
        else {
          // promise was rejected
          baton->result = -1;
          baton->done = false;
        }
      }
      NAN_GETTER(GitCheckoutOptions::GetNotifyPayload) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->notify_payload);

     }

    NAN_SETTER(GitCheckoutOptions::SetNotifyPayload) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanAssignPersistent(wrapper->notify_payload, value);

     }

      NAN_GETTER(GitCheckoutOptions::GetProgressCb) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->progress_cb->GetFunction());

     }

    NAN_SETTER(GitCheckoutOptions::SetProgressCb) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (value->IsFunction()) {
          wrapper->progress_cb = new NanCallback(value.As<Function>());
        }

     }

      int GitCheckoutOptions::progress_cb_cppCallback (
          const char * path,           size_t completed_steps,           size_t total_steps,           void * payload        ) {
        ProgressCbBaton* baton = new ProgressCbBaton();

          baton->path = path;
          baton->completed_steps = completed_steps;
          baton->total_steps = total_steps;
          baton->payload = payload;
 
        baton->req.data = baton;
        baton->done = false;

        uv_queue_work(uv_default_loop(), &baton->req, progress_cb_asyncWork, progress_cb_asyncAfter);

        while(!baton->done) {
          this_thread::sleep_for(chrono::milliseconds(1));
        }

 
        return baton->result;
      }

      void GitCheckoutOptions::progress_cb_asyncWork(uv_work_t* req) {
        // We aren't doing any work on a seperate thread, just need to
        // access the main node thread in the async after method.
        // However, this worker method is still needed
      }

      void GitCheckoutOptions::progress_cb_asyncAfter(uv_work_t* req, int status) {
        NanScope();

        ProgressCbBaton* baton = static_cast<ProgressCbBaton*>(req->data);
        GitCheckoutOptions* instance = static_cast<GitCheckoutOptions*>(baton->payload);

        if (instance->progress_cb->IsEmpty()) {
            baton->result = 1; // no results acquired
 
          baton->done = true;
          return;
        }

        Local<Value> argv[4] = {
                 NanNew(baton->path),
                  // HACK: NAN should really have an overload for NanNew to support size_t
                NanNew((unsigned int)baton->completed_steps),
                  // HACK: NAN should really have an overload for NanNew to support size_t
                NanNew((unsigned int)baton->total_steps),
                NanNew(instance->progress_payload)
          };

        TryCatch tryCatch;
        Handle<Value> result = instance->progress_cb->Call(4, argv);

        if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) {
          Handle<Value> thenProp = result->ToObject()->Get(NanNew("then"));

          if (thenProp->IsFunction()) {
            // we can be reasonbly certain that the result is a promise
            Local<Object> promise = result->ToObject();

            NanAssignPersistent(baton->promise, promise);

            uv_queue_work(uv_default_loop(), &baton->req, progress_cb_asyncWork, progress_cb_asyncPromisePolling);
            return;
          }
        }

        int resultStatus;

         baton->done = true;
      }

      void GitCheckoutOptions::progress_cb_asyncPromisePolling(uv_work_t* req, int status) {
        NanScope();

        ProgressCbBaton* baton = static_cast<ProgressCbBaton*>(req->data);
        Local<Object> promise = NanNew<Object>(baton->promise);
        NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As<Function>());
        Local<Value> argv[1]; // MSBUILD won't assign an array of length 0
        Local<Boolean> isPending = isPendingFn->Call(0, argv)->ToBoolean();

        if (isPending->Value()) {
          uv_queue_work(uv_default_loop(), &baton->req, progress_cb_asyncWork, progress_cb_asyncPromisePolling);
          return;
        }

        NanCallback* isFulfilledFn = new NanCallback(promise->Get(NanNew("isFulfilled")).As<Function>());
        Local<Boolean> isFulfilled = isFulfilledFn->Call(0, argv)->ToBoolean();

        if (isFulfilled->Value()) {
          NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As<Function>());
          Handle<Value> result = resultFn->Call(0, argv);
          int resultStatus;

           baton->done = true;
        }
        else {
          // promise was rejected
          baton->result = -1;
          baton->done = false;
        }
      }
      NAN_GETTER(GitCheckoutOptions::GetProgressPayload) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->progress_payload);

     }

    NAN_SETTER(GitCheckoutOptions::SetProgressPayload) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanAssignPersistent(wrapper->progress_payload, value);

     }

      NAN_GETTER(GitCheckoutOptions::GetPaths) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->paths);

     }

    NAN_SETTER(GitCheckoutOptions::SetPaths) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanDisposePersistent(wrapper->paths);
        wrapper->raw->paths = * ObjectWrap::Unwrap<GitStrarray>(value->ToObject())->GetValue();

     }

      NAN_GETTER(GitCheckoutOptions::GetBaseline) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanReturnValue(wrapper->baseline);

     }

    NAN_SETTER(GitCheckoutOptions::SetBaseline) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        NanDisposePersistent(wrapper->baseline);
        wrapper->raw->baseline =  ObjectWrap::Unwrap<GitTree>(value->ToObject())->GetValue();

     }

      NAN_GETTER(GitCheckoutOptions::GetTargetDirectory) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->target_directory) {
          NanReturnValue(NanNew<String>(wrapper->GetValue()->target_directory));
        }
        else {
          NanReturnUndefined();
        }

     }

    NAN_SETTER(GitCheckoutOptions::SetTargetDirectory) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->target_directory) {
        }

        String::Utf8Value str(value);
        wrapper->GetValue()->target_directory = strdup(*str);

     }

      NAN_GETTER(GitCheckoutOptions::GetAncestorLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->ancestor_label) {
          NanReturnValue(NanNew<String>(wrapper->GetValue()->ancestor_label));
        }
        else {
          NanReturnUndefined();
        }

     }

    NAN_SETTER(GitCheckoutOptions::SetAncestorLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->ancestor_label) {
        }

        String::Utf8Value str(value);
        wrapper->GetValue()->ancestor_label = strdup(*str);

     }

      NAN_GETTER(GitCheckoutOptions::GetOurLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->our_label) {
          NanReturnValue(NanNew<String>(wrapper->GetValue()->our_label));
        }
        else {
          NanReturnUndefined();
        }

     }

    NAN_SETTER(GitCheckoutOptions::SetOurLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->our_label) {
        }

        String::Utf8Value str(value);
        wrapper->GetValue()->our_label = strdup(*str);

     }

      NAN_GETTER(GitCheckoutOptions::GetTheirLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->their_label) {
          NanReturnValue(NanNew<String>(wrapper->GetValue()->their_label));
        }
        else {
          NanReturnUndefined();
        }

     }

    NAN_SETTER(GitCheckoutOptions::SetTheirLabel) {
      NanScope();

      GitCheckoutOptions *wrapper = ObjectWrap::Unwrap<GitCheckoutOptions>(args.This());

        if (wrapper->GetValue()->their_label) {
        }

        String::Utf8Value str(value);
        wrapper->GetValue()->their_label = strdup(*str);

     }

   
Persistent<Function> GitCheckoutOptions::constructor_template;
