#define NODE_ARGUMENTS_TYPE v8::FunctionCallbackInfo<v8::Value>
#define NODE_ARGUMENTS const NODE_ARGUMENTS_TYPE&
#define NODE_METHOD(name) static void name(NODE_ARGUMENTS info)
#define NODE_GETTER(name) static void name(v8::Local<v8::String> _, const v8::PropertyCallbackInfo<v8::Value>& info)
#define REGISTER(name) friend void RegisterModule(v8::Local<v8::Object> exports, v8::Local<v8::Object> module); static void name(v8::Isolate* isolate, v8::Local<v8::Object> exports, v8::Local<v8::Object> module)

#define NewHandleScope v8::HandleScope scope(isolate)
#define EasyIsolate v8::Isolate* isolate = v8::Isolate::GetCurrent()
#define UseIsolate v8::Isolate* isolate = info.GetIsolate()
#define UseContext v8::Local<v8::Context> ctx = isolate->GetCurrentContext()
#define UseIsolateAndContext UseIsolate; UseContext
#define OnlyIsolate info.GetIsolate()
#define OnlyContext isolate->GetCurrentContext()
#define OnlyIsolateAndContext info.GetIsolate()->GetCurrentContext()

#define Unwrap node::ObjectWrap::Unwrap

inline v8::Local<v8::String> StringFromUtf8(v8::Isolate* isolate, const char* data, int length) {
	return v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal, length).ToLocalChecked();
}
inline v8::Local<v8::String> InternalizedFromUtf8(v8::Isolate* isolate, const char* data, int length) {
	return v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kInternalized, length).ToLocalChecked();
}

inline v8::Local<v8::Value> InternalizedFromUtf8OrNull(v8::Isolate* isolate, const char* data, int length) {
	if (data == NULL) return v8::Null(isolate);
	return InternalizedFromUtf8(isolate, data, length);
}

inline void SetFrozen(v8::Isolate* isolate, v8::Local<v8::Context> ctx, v8::Local<v8::Object> obj, ConstantString& key, v8::Local<v8::Value> value) {
	static const v8::PropertyAttribute FROZEN_PROPERTY = static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
	obj->DefineOwnProperty(ctx, CS::Get(isolate, key), value, FROZEN_PROPERTY).FromJust();
}

void ThrowError(const char* message) { EasyIsolate; isolate->ThrowException(v8::Exception::Error(StringFromUtf8(isolate, message, -1))); }
void ThrowTypeError(const char* message) { EasyIsolate; isolate->ThrowException(v8::Exception::TypeError(StringFromUtf8(isolate, message, -1))); }
void ThrowRangeError(const char* message) { EasyIsolate; isolate->ThrowException(v8::Exception::RangeError(StringFromUtf8(isolate, message, -1))); }

#define REQUIRE_ARGUMENT_ANY(at, var)                                          \
	if (info.Length() <= (at()))                                               \
		return ThrowTypeError("Expected a "#at" argument");                    \
	var = info[at()]

#define _REQUIRE_ARGUMENT(at, var, Type, message, ...)                         \
	if (info.Length() <= (at()) || !info[at()]->Is##Type())                    \
		return ThrowTypeError("Expected "#at" argument to be "#message);       \
	var = v8::Local<v8::Type>::Cast(info[at()])__VA_ARGS__

#define REQUIRE_ARGUMENT_INT32(at, var)                                        \
	_REQUIRE_ARGUMENT(at, var, Int32, a 32-bit signed integer, ->Value())
#define REQUIRE_ARGUMENT_BOOLEAN(at, var)                                      \
	_REQUIRE_ARGUMENT(at, var, Boolean, a boolean, ->Value())
#define REQUIRE_ARGUMENT_STRING(at, var)                                       \
	_REQUIRE_ARGUMENT(at, var, String, a string)
#define REQUIRE_ARGUMENT_OBJECT(at, var)                                       \
	_REQUIRE_ARGUMENT(at, var, Object, an object)
#define REQUIRE_ARGUMENT_FUNCTION(at, var)                                     \
	_REQUIRE_ARGUMENT(at, var, Function, a function)

#define REQUIRE_DATABASE_OPEN(db)                                              \
	if (!db->open)                                                             \
		return ThrowTypeError("The database connection is not open")
#define REQUIRE_DATABASE_NOT_BUSY(db)                                          \
	if (db->busy)                                                              \
		return ThrowTypeError("This database connection is busy executing a query")
#define REQUIRE_DATABASE_NO_ITERATORS(db)                                      \
	if (db->iterators)                                                         \
		return ThrowTypeError("This database connection is busy executing a query")
#define REQUIRE_STATEMENT_NOT_LOCKED(stmt)                                     \
	if (stmt->locked)                                                          \
		return ThrowTypeError("This statement is busy executing a query")

#define first() 0
#define second() 1
#define third() 2
#define fourth() 3
#define fifth() 4
#define sixth() 5
#define seventh() 6
#define eighth() 7
#define ninth() 8
#define tenth() 9

#hdr
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 6 ||                      \
	(V8_MAJOR_VERSION == 6 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 2))
#define EXTRACT_STRING(isolate, string) isolate, string
#else
#define EXTRACT_STRING(_unused, string) string
#endif
#end

// Returns a std:string of the concatenation of 3 well-formed C-strings.
std::string CONCAT(const char* a, const char* b, const char* c) {
	std::string result(a);
	result += b;
	result += c;
	return result;
}

// Returns a copy of a well-formed C-string.
const char* COPY(const char* source) {
	size_t bytes = strlen(source) + 1;
	char* dest = new char[bytes];
	memcpy(dest, source, bytes);
	return dest;
}

// Allocates an empty array, without calling constructors/initializers.
template<class T> inline T* ALLOC_ARRAY(size_t count) {
	return static_cast<T*>(::operator new[](count * sizeof(T)));
}

// Deallocates an array, without calling destructors.
template<class T> inline void FREE_ARRAY(T* array_pointer) {
	::operator delete[](array_pointer);
}

v8::Local<v8::Value> Require(v8::Local<v8::Object> module, const char* path) {
	EasyIsolate;
	UseContext;
	v8::Local<v8::Function> require = v8::Local<v8::Function>::Cast(module->Get(ctx, StringFromUtf8(isolate, "require", -1)).ToLocalChecked());
	v8::Local<v8::Value> requireArg = StringFromUtf8(isolate, path, -1);
	return require->Call(ctx, module, 1, &requireArg).ToLocalChecked();
}

inline void NODE_SET_PROTOTYPE_GETTER(v8::Local<v8::FunctionTemplate> recv, const char* name, v8::AccessorGetterCallback getter) {
	EasyIsolate;
	NewHandleScope;
	recv->InstanceTemplate()->SetAccessor(
		StringFromUtf8(isolate, name, -1),
		getter,
		0,
		v8::Local<v8::Value>(),
		v8::AccessControl::ALL_CAN_READ,
		v8::PropertyAttribute::None,
		v8::AccessorSignature::New(isolate, recv)
	);
}

inline void NODE_SET_PROTOTYPE_SYMBOL_METHOD(v8::Local<v8::FunctionTemplate> recv, v8::Local<v8::Symbol> symbol, v8::FunctionCallback callback) {
	EasyIsolate;
	NewHandleScope;
	recv->PrototypeTemplate()->Set(symbol, v8::FunctionTemplate::New(
		isolate,
		callback,
		v8::Local<v8::Value>(),
		v8::Signature::New(isolate, recv)
	));
}

#hdr
template <class T> using CopyablePersistent = v8::Persistent<T, v8::CopyablePersistentTraits<T>>;
#end
