#include "tlscontext.js.h"

#include "log.h"
#include "mem.h"
#include "task.h"
#include "tls.h"

#include <stdlib.h>
#include <string.h>

static JSClassID _classId;
static int _count;

typedef struct _tf_tls_context_t
{
	tf_tls_context_t* context;
	tf_task_t* task;
	JSValue object;
} tf_tls_context_t;

static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static void _tls_context_finalizer(JSRuntime* runtime, JSValue value);

static JSValue _tls_context_set_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
	tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
	const char* value = JS_ToCString(context, argv[0]);
	tf_tls_context_set_certificate(tls->context, value);
	JS_FreeCString(context, value);
	return JS_UNDEFINED;
}

static JSValue _tls_context_set_private_key(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
	tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
	const char* value = JS_ToCString(context, argv[0]);
	tf_tls_context_set_private_key(tls->context, value);
	JS_FreeCString(context, value);
	return JS_UNDEFINED;
}

static JSValue _tls_context_add_trusted_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
	tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
	const char* value = JS_ToCString(context, argv[0]);
	tf_tls_context_add_trusted_certificate(tls->context, value);
	JS_FreeCString(context, value);
	return JS_UNDEFINED;
}

JSValue tf_tls_context_register(JSContext* context)
{
	JS_NewClassID(&_classId);
	JSClassDef def = {
		.class_name = "TlsContext",
		.finalizer = _tls_context_finalizer,
	};
	if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0)
	{
		fprintf(stderr, "Failed to register TlsContext.\n");
	}
	return JS_NewCFunction2(context, _tls_context_create, "TlsContext", 0, JS_CFUNC_constructor, 0);
}

tf_tls_context_t* tf_tls_context_get(JSValue value)
{
	tf_tls_context_t* tls = JS_GetOpaque(value, _classId);
	return tls ? tls->context : NULL;
}

int tf_tls_context_get_count()
{
	return _count;
}

static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
	tf_tls_context_t* tls = tf_malloc(sizeof(tf_tls_context_t));
	memset(tls, 0, sizeof(*tls));

	++_count;
	tls->object = JS_NewObjectClass(context, _classId);
	JS_SetOpaque(tls->object, tls);

	JS_SetPropertyStr(context, tls->object, "setCertificate", JS_NewCFunction(context, _tls_context_set_certificate, "setCertificate", 1));
	JS_SetPropertyStr(context, tls->object, "setPrivateKey", JS_NewCFunction(context, _tls_context_set_private_key, "setPrivateKey", 1));
	JS_SetPropertyStr(context, tls->object, "addTrustedCertificate", JS_NewCFunction(context, _tls_context_add_trusted_certificate, "addTrustedCertificate", 1));

	tls->context = tf_tls_context_create();
	tls->task = tf_task_get(context);

	return tls->object;
}

static void _tls_context_finalizer(JSRuntime* runtime, JSValue value)
{
	tf_tls_context_t* tls = JS_GetOpaque(value, _classId);
	if (tls->context)
	{
		tf_tls_context_destroy(tls->context);
		tls->context = NULL;
	}
	--_count;
	tf_free(tls);
}
