<commit_before>#include <errno.h>
#include <node_buffer.h>

#include "btleConnection.h"
#include "connection.h"
#include "btio.h"
#include "btleException.h"
#include "util.h"

using namespace v8;
using namespace node;

// Constructor definition
Persistent<Function>
BTLEConnection::constructor;

// Callback data structure
struct callbackData {
  callbackData() : conn(NULL), data(NULL), startHandle(0), endHandle(0) {}
  BTLEConnection* conn;
  void* data;
  uint16_t startHandle;
  uint16_t endHandle;
};

// Constructor
BTLEConnection::BTLEConnection() : att(NULL), connection(NULL)
{
}

// Destructor
BTLEConnection::~BTLEConnection()
{
  delete att;
  delete connection;
}

// Node.js initialization
void
BTLEConnection::Init()
{
  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("Connection"));
  tpl->InstanceTemplate()->SetInternalFieldCount(4);

  constructor = Persistent<Function>::New(tpl->GetFunction());
}

// Node.js new object construction
Handle<Value>
BTLEConnection::New(const Arguments& args)
{
  HandleScope scope;

  assert(args.IsConstructCall());

  BTLEConnection* conn = new BTLEConnection();
  conn->self = Persistent<Object>::New(args.This());
  conn->Wrap(args.This());

  return scope.Close(args.This());
}

// Connect node.js method
Handle<Value>
BTLEConnection::Connect(const Arguments& args)
{
  HandleScope scope;
  struct set_opts opts;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Destination object must be a string")));
    return scope.Close(Undefined());
  }

  Local<String> destination = Local<String>::Cast(args[0]);

  Local<Object> options;
  Persistent<Function> callback;

  if (args[1]->IsObject() && !args[1]->IsFunction()) {
    if (args.Length() < 3) {
      ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
      return scope.Close(Undefined());
    }
    if (!args[2]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
      return scope.Close(Undefined());
    }
    options = args[1]->ToObject();
    callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
  } else if (args[1]->IsFunction()) {
    options = Object::New();
    callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  } else {
      ThrowException(Exception::TypeError(String::New("Second argument must be either a callback or options")));
      return scope.Close(Undefined());
  }

  if (!setOpts(opts, destination, options)) {
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  callback.MakeWeak(*callback, weak_cb);
  conn->connectionCallback = callback;

  conn->connection = new Connection();
  conn->att = new Att(conn->connection);
  conn->att->onError(onError, conn);
  try {
    conn->connection->connect(opts, onConnect, (void*) conn);
  } catch (BTLEException& e) {
    conn->emit_error();
  }

  return scope.Close(Undefined());
}

// Send a Find Information request
Handle<Value>
BTLEConnection::FindInformation(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 3) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->findInformation(startHandle, endHandle, onFindInformation, cd);
  return scope.Close(Undefined());
}

// Send a Find By Type Value request
Handle<Value>
BTLEConnection::FindByTypeValue(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 5) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[3])) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a buffer")));
    return scope.Close(Undefined());
  }

  if (!args[4]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fifth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[4]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->findByTypeValue(startHandle, endHandle, uuid,
      (const uint8_t*) Buffer::Data(args[3]), Buffer::Length(args[3]), onFindByType, cd);
  return scope.Close(Undefined());
}

// Send a Read By Type request
Handle<Value>
BTLEConnection::ReadByType(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 4) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!args[3]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[3]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->readByType(startHandle, endHandle, uuid, onReadByType, cd);
  return scope.Close(Undefined());
}

// Send a Read By Group Type request
Handle<Value>
BTLEConnection::ReadByGroupType(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 4) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!args[3]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[3]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  // Note: Can re-use onReadByType
  conn->att->readByGroupType(startHandle, endHandle, uuid, onReadByGroupType, cd);
  return scope.Close(Undefined());
}

// Read an attribute
Handle<Value>
BTLEConnection::ReadHandle(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  callback.MakeWeak(*callback, weak_cb);
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;

  int handle;
  getIntValue(args[0]->ToNumber(), handle);
  conn->att->readAttribute(handle, onReadAttribute, cd);
  return scope.Close(Undefined());
}

// Write an attribute without a response
Handle<Value>
BTLEConnection::WriteCommand(const v8::Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[1])) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a buffer")));
    return scope.Close(Undefined());
  }

  Persistent<Function> callback;
  if (args.Length() > 2) {
    if (!args[2]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
      return scope.Close(Undefined());
    }

    callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
    callback.MakeWeak(*callback, weak_cb);
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  int handle;
  getIntValue(args[0]->ToNumber(), handle);

  struct callbackData* cd = new struct callbackData();
  cd->conn = conn;
  if (args.Length() > 2) {
    cd->data = *callback;
  }

  conn->att->writeCommand(handle, (const uint8_t*) Buffer::Data(args[1]), Buffer::Length(args[1]),
      onWrite, cd);

  return scope.Close(Undefined());
}

// Write an attribute with a response
Handle<Value>
BTLEConnection::WriteRequest(const v8::Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[1])) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a buffer")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  int handle;
  getIntValue(args[0]->ToNumber(), handle);

  conn->att->writeRequest(handle, (const uint8_t*) Buffer::Data(args[1]), Buffer::Length(args[1]));

  return scope.Close(Undefined());
}

// Add a listener for notifications
Handle<Value>
BTLEConnection::AddNotificationListener(const Arguments& args)
{
  HandleScope scope;

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a callback")));
    return scope.Close(Undefined());
  }

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  callback.MakeWeak(*callback, weak_cb);

  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;

  int handle;
  getIntValue(args[0]->ToNumber(), handle);
  conn->att->listenForNotifications(handle, onReadNotification, cd);

  return scope.Close(Undefined());
}

// Close the connection
Handle<Value>
BTLEConnection::Close(const Arguments& args)
{
  HandleScope scope;

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  if (args.Length() > 0) {
    if (!args[0]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Argument must be a callback")));
      return scope.Close(Undefined());
    } else {
      Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
      callback.MakeWeak(*callback, weak_cb);
      conn->closeCallback = callback;
    }
  }

  if (conn->att) {
    conn->connection->close(onClose, (void*) conn);
  }

  return scope.Close(Undefined());
}

Local<Object>
BTLEConnection::getAttribute(Attribute* attribute)
{
  Local<Object> ret = Object::New();
  ret->Set(String::New("handle"), attribute->getHandle() > 0 ? Integer::New(attribute->getHandle()) : Null());
  if (!attribute->containsType()) {
    ret->Set(String::New("type"), Null());
  } else {
    char buffer[128];
    bt_uuid_to_string(&attribute->getType(), buffer, sizeof(buffer));
    ret->Set(String::New("type"), String::New(buffer));
  }
  size_t vlen = 0;
  const uint8_t* value = attribute->getValue(vlen);
  if (value == NULL) {
    ret->Set(String::New("value"), Null());
  } else {
    ret->Set(String::New("value"), Buffer::New(String::New((char*) value, vlen)));
  }

  return ret;
}

// Emit an 'error' event
void
BTLEConnection::emit_error()
{
    uv_err_t err = uv_last_error(uv_default_loop());
    const int argc = 3;
    Local<Value> error = ErrnoException(errno, "connect", uv_strerror(err));
    Local<Value> argv[argc] = { String::New("error"),
                                error,
                                Local<Value>::New(this->self) };
    MakeCallback(this->self, "emit", argc, argv);
}

void
BTLEConnection::emit_error(const char* errorMessage)
{
    const int argc = 3;
    Local<Value> error = String::New(errorMessage);
    Local<Value> argv[argc] = { String::New("error"),<commit_msg>Fixed problem with buffer being sent back<commit_after>#include <errno.h>
#include <node_buffer.h>

#include "btleConnection.h"
#include "connection.h"
#include "btio.h"
#include "btleException.h"
#include "util.h"

using namespace v8;
using namespace node;

// Constructor definition
Persistent<Function>
BTLEConnection::constructor;

// Callback data structure
struct callbackData {
  callbackData() : conn(NULL), data(NULL), startHandle(0), endHandle(0) {}
  BTLEConnection* conn;
  void* data;
  uint16_t startHandle;
  uint16_t endHandle;
};

// Constructor
BTLEConnection::BTLEConnection() : att(NULL), connection(NULL)
{
}

// Destructor
BTLEConnection::~BTLEConnection()
{
  delete att;
  delete connection;
}

// Node.js initialization
void
BTLEConnection::Init()
{
  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("Connection"));
  tpl->InstanceTemplate()->SetInternalFieldCount(4);

  constructor = Persistent<Function>::New(tpl->GetFunction());
}

// Node.js new object construction
Handle<Value>
BTLEConnection::New(const Arguments& args)
{
  HandleScope scope;

  assert(args.IsConstructCall());

  BTLEConnection* conn = new BTLEConnection();
  conn->self = Persistent<Object>::New(args.This());
  conn->Wrap(args.This());

  return scope.Close(args.This());
}

// Connect node.js method
Handle<Value>
BTLEConnection::Connect(const Arguments& args)
{
  HandleScope scope;
  struct set_opts opts;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Destination object must be a string")));
    return scope.Close(Undefined());
  }

  Local<String> destination = Local<String>::Cast(args[0]);

  Local<Object> options;
  Persistent<Function> callback;

  if (args[1]->IsObject() && !args[1]->IsFunction()) {
    if (args.Length() < 3) {
      ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
      return scope.Close(Undefined());
    }
    if (!args[2]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
      return scope.Close(Undefined());
    }
    options = args[1]->ToObject();
    callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
  } else if (args[1]->IsFunction()) {
    options = Object::New();
    callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  } else {
      ThrowException(Exception::TypeError(String::New("Second argument must be either a callback or options")));
      return scope.Close(Undefined());
  }

  if (!setOpts(opts, destination, options)) {
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  callback.MakeWeak(*callback, weak_cb);
  conn->connectionCallback = callback;

  conn->connection = new Connection();
  conn->att = new Att(conn->connection);
  conn->att->onError(onError, conn);
  try {
    conn->connection->connect(opts, onConnect, (void*) conn);
  } catch (BTLEException& e) {
    conn->emit_error();
  }

  return scope.Close(Undefined());
}

// Send a Find Information request
Handle<Value>
BTLEConnection::FindInformation(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 3) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->findInformation(startHandle, endHandle, onFindInformation, cd);
  return scope.Close(Undefined());
}

// Send a Find By Type Value request
Handle<Value>
BTLEConnection::FindByTypeValue(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 5) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[3])) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a buffer")));
    return scope.Close(Undefined());
  }

  if (!args[4]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fifth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[4]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->findByTypeValue(startHandle, endHandle, uuid,
      (const uint8_t*) Buffer::Data(args[3]), Buffer::Length(args[3]), onFindByType, cd);
  return scope.Close(Undefined());
}

// Send a Read By Type request
Handle<Value>
BTLEConnection::ReadByType(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 4) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!args[3]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[3]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  conn->att->readByType(startHandle, endHandle, uuid, onReadByType, cd);
  return scope.Close(Undefined());
}

// Send a Read By Group Type request
Handle<Value>
BTLEConnection::ReadByGroupType(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 4) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[2]->IsString()) {
    ThrowException(Exception::TypeError(String::New("Third argument must be a string")));
    return scope.Close(Undefined());
  }

  if (!args[3]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Fourth argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[3]));
  callback.MakeWeak(*callback, weak_cb);

  int startHandle, endHandle;
  getIntValue(args[0]->ToNumber(), startHandle);
  getIntValue(args[1]->ToNumber(), endHandle);

  bt_uuid_t uuid;
  bt_string_to_uuid(&uuid, getStringValue(args[2]->ToString()));
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;
  cd->startHandle = startHandle;
  cd->endHandle = endHandle;

  // Note: Can re-use onReadByType
  conn->att->readByGroupType(startHandle, endHandle, uuid, onReadByGroupType, cd);
  return scope.Close(Undefined());
}

// Read an attribute
Handle<Value>
BTLEConnection::ReadHandle(const Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a callback")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  callback.MakeWeak(*callback, weak_cb);
  
  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;

  int handle;
  getIntValue(args[0]->ToNumber(), handle);
  conn->att->readAttribute(handle, onReadAttribute, cd);
  return scope.Close(Undefined());
}

// Write an attribute without a response
Handle<Value>
BTLEConnection::WriteCommand(const v8::Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[1])) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a buffer")));
    return scope.Close(Undefined());
  }

  Persistent<Function> callback;
  if (args.Length() > 2) {
    if (!args[2]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Third argument must be a callback")));
      return scope.Close(Undefined());
    }

    callback = Persistent<Function>::New(Local<Function>::Cast(args[2]));
    callback.MakeWeak(*callback, weak_cb);
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  int handle;
  getIntValue(args[0]->ToNumber(), handle);

  struct callbackData* cd = new struct callbackData();
  cd->conn = conn;
  if (args.Length() > 2) {
    cd->data = *callback;
  }

  conn->att->writeCommand(handle, (const uint8_t*) Buffer::Data(args[1]), Buffer::Length(args[1]),
      onWrite, cd);

  return scope.Close(Undefined());
}

// Write an attribute with a response
Handle<Value>
BTLEConnection::WriteRequest(const v8::Arguments& args)
{
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!Buffer::HasInstance(args[1])) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a buffer")));
    return scope.Close(Undefined());
  }

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  int handle;
  getIntValue(args[0]->ToNumber(), handle);

  conn->att->writeRequest(handle, (const uint8_t*) Buffer::Data(args[1]), Buffer::Length(args[1]));

  return scope.Close(Undefined());
}

// Add a listener for notifications
Handle<Value>
BTLEConnection::AddNotificationListener(const Arguments& args)
{
  HandleScope scope;

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsUint32()) {
    ThrowException(Exception::TypeError(String::New("First argument must be a handle number")));
    return scope.Close(Undefined());
  }

  if (!args[1]->IsFunction()) {
    ThrowException(Exception::TypeError(String::New("Second argument must be a callback")));
    return scope.Close(Undefined());
  }

  Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
  callback.MakeWeak(*callback, weak_cb);

  struct callbackData* cd = new struct callbackData();
  cd->data = *callback;
  cd->conn = conn;

  int handle;
  getIntValue(args[0]->ToNumber(), handle);
  conn->att->listenForNotifications(handle, onReadNotification, cd);

  return scope.Close(Undefined());
}

// Close the connection
Handle<Value>
BTLEConnection::Close(const Arguments& args)
{
  HandleScope scope;

  BTLEConnection* conn = ObjectWrap::Unwrap<BTLEConnection>(args.This());

  if (args.Length() > 0) {
    if (!args[0]->IsFunction()) {
      ThrowException(Exception::TypeError(String::New("Argument must be a callback")));
      return scope.Close(Undefined());
    } else {
      Persistent<Function> callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
      callback.MakeWeak(*callback, weak_cb);
      conn->closeCallback = callback;
    }
  }

  if (conn->att) {
    conn->connection->close(onClose, (void*) conn);
  }

  return scope.Close(Undefined());
}

Local<Object>
BTLEConnection::getAttribute(Attribute* attribute)
{
  Local<Object> ret = Object::New();
  ret->Set(String::New("handle"), attribute->getHandle() > 0 ? Integer::New(attribute->getHandle()) : Null());
  if (!attribute->containsType()) {
    ret->Set(String::New("type"), Null());
  } else {
    char buffer[128];
    bt_uuid_to_string(&attribute->getType(), buffer, sizeof(buffer));
    ret->Set(String::New("type"), String::New(buffer));
  }
  size_t vlen = 0;
  const uint8_t* value = attribute->getValue(vlen);
  if (value == NULL) {
    ret->Set(String::New("value"), Null());
  } else {
    Buffer* buf = Buffer::New((char*) value, vlen);
    ret->Set(String::New("value"), buf->handle_);
  }

  return ret;
}

// Emit an 'error' event
void
BTLEConnection::emit_error()
{
    uv_err_t err = uv_last_error(uv_default_loop());
    const int argc = 3;
    Local<Value> error = ErrnoException(errno, "connect", uv_strerror(err));
    Local<Value> argv[argc] = { String::New("error"),
                                error,
                                Local<Value>::New(this->self) };
    MakeCallback(this->self, "emit", argc, argv);
}

void
BTLEConnection::emit_error(const char* errorMessage)
{
    const int argc = 3;
    Local<Value> error = String::New(errorMessage);
    Local<Value> argv[argc] = { String::New("error"),
{'fraction_non_alphanumeric': 0.16637649286783346, 'fraction_numerical': 0.00745288493845261, 'mean_word_length': 5.121914734480179, 'pattern_counts': {'":': 0, '<': 155, '<?xml version=': 0, '>': 370, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7448372', 'n_tokens_mistral': 10131, 'n_tokens_neox': 9936, 'n_words': 2046}