(* [ʞ] src/janet-lang.gcd vi:ft=d
 *  ~ lexi hale <lexi@hale.su>
 *  🄯 AGPLv3
 *  ? implement the godot-janet interface
 *)

use <janet.h>;

class JanetLang is ScriptLanguageExtension {
	use <stdio.h>;
	use "util.h";

	new {};

	impl _get_name() -> string {
		gd_string j;
		_t(string).newWithUtf8Chars(&j, "Janet");
		return j;
	};

	impl _get_extension() -> string {
		gd_string j;
		_t(string).newWithUtf8Chars(&j, "janet");
		return j;
	};

	impl _supports_documentation() -> bool { return true; };
	impl _supports_builtin_mode()  -> bool { return true; };
	impl _is_using_templates()     -> bool { return false; };
	impl _can_inherit_from_file()  -> bool { return true; };

	impl _handles_global_class_type(string t) -> bool { return false; };
	impl _get_type() -> string {
		gd_string s = {};
		_t(string).newWithUtf8Chars(&s, "JanetScriptText");
		return s;
	};
	impl _get_recognized_extensions() -> packed-string-array {
		gd_packedStringArray r = {};
		_t(packedStringArray).empty(&r, nullptr);
		_gdu_array_string_pushLit(&r, "janet");
		_gdu_array_string_pushLit(&r, "jimage");
		return r;
	};
	impl _get_comment_delimiters() -> packed-string-array {
		gd_packedStringArray r = {};
		_t(packedStringArray).empty(&r, nullptr);
		_gdu_array_string_pushLit(&r, "#");
		return r;
	};
	impl _get_string_delimiters() -> packed-string-array {
		gd_packedStringArray r = {};
		_t(packedStringArray).empty(&r, nullptr);
		_gdu_array_string_pushLit(&r, "\" \"");
		_gdu_array_string_pushLit(&r, "```` ````");
		_gdu_array_string_pushLit(&r, "``` ```");
		_gdu_array_string_pushLit(&r, "`` ``");
		_gdu_array_string_pushLit(&r, "` `");
		(* et cetera ad infinitum *)
		return r;
	};
	impl _is_control_flow_keyword(string k) -> bool {
		#define l(s) ((pstr){(s),sizeof(s)})
		const pstr words[] = {
			l("if"), l("cond"), l("when"), l("unless"),
			l("loop"), l("each"),
			l("for"), l("forv"), l("forever"),
			l("seq"), l("catseq"),
			(* et cetera ad nauseam *)
		};
		#undef l
		for (size_t i = 0; i < _sz(words); ++i) {
			if (gdu_strEq_sz(&k, words[i].v, words[i].sz))
				return true;
		}
		return false;
	};
	impl _get_reserved_words() -> packed-string-array {
		typedef struct {const char* w; size_t sz;} pstr;
		#define l(s) ((pstr){(s),sizeof(s)})
		const pstr words[] = {
			l("if"), l("cond"),
			l("def"), l("defn"), l("defmacro"),
			l("fn"),
			l("var"), l("let"),
			l("loop"), l("each"),
			l("for"), l("forv"), l("forever"),
			l("seq"), l("catseq"),
			l("map"), l("mapcat"),
			l("find"),
			l("array"), l("tuple"),
			l("string"), l("buffer"),
			l("table"), l("struct"),
			(* et cetera ad nauseam *)
		};
		gd_packedStringArray r = {};
		_t(packedStringArray).empty(&r, nullptr);
		for (size_t i = 0; i < _sz(words); ++i) {
			gdu_array_string_pushPtr(&r, words[i].w, words[i].sz);
		}
		return r;
		#undef l
	};

	impl _validate_path(string path) -> string {
		gd_string s = {};
		_t(string).empty(&s, nullptr);
		return s;
	};


	impl _make_template
	(	string tpl;
		string class;
		string base;
	) -> ref Script {
		auto janscr = gdjn_class_JanetScriptText_new();
		return janscr -> self;
	};
	impl _create_script() -> ref Object {
		auto janscr = gdjn_class_JanetScriptText_new();
		return janscr -> self;
	};

	impl _get_documentation() -> array[dictionary] {
		gd_array a = {};
		_t(array).ref(&a, &gdjn_ctx -> gd.dox);
		return gdjn_ctx -> gd.dox;
	};

	impl _init() { /* (* "deprecated" but i still have to impl it?? *) */ }; 
	impl _frame() {};
	impl _thread_enter() { janet_init(); };
	impl _thread_exit() { janet_deinit(); };
	impl _finish() {};

	impl _overrides_external_editor() -> bool { return false; };
	impl _get_global_class_name(string path) -> dictionary {
		gd_dictionary dict;
		_t(dictionary).empty(&dict,nullptr);
		return dict;
		(* FIXME *)
	};
	impl _validate(
		string script;
		string path;
		bool vFuncs;
		bool vErrs;
		bool vWarns;
		bool vSafe;
	) -> dictionary {
		gd_dictionary dict;
		_t(dictionary).empty(&dict,nullptr);
		return dict;
	};
};

class JanetScript is ScriptExtension {
	var as string: path;
	new {
		_t(string).empty(&me -> path, nullptr);
	};
	del {
		_t(string).dtor(&me -> path);
	};

	impl _create_instance(array[variant] argv, int argc, ref Object owner, bool refCounted, int error) -> ref Object {

	};
	impl _get_language() -> ref ScriptLanguage {
		return gdjn_ctx -> gd.janetLang_inst;
	};
	impl _set_path(string path, bool takeOver) {
		if (takeOver) {
			_t(string).dtor(&me -> path);
			me -> path = path;
		} else {
			_t(string).dtor(&me -> path);
			_t(string).copy(&me -> path, (void const*[]) {&path});
		}
	};
	impl _get_base_script() -> ref Script {
		return nullptr;
	};
	impl _has_static_method(string-name method) -> bool {
		return false;
	};
	impl _is_tool() -> bool { return false; }; (* FIXME *)
};

class JanetScriptText extends JanetScript {
	var as string: src;
	new {
		_t(string).empty(&me -> src, nullptr);
	};
	del {
		_t(string).dtor(&me -> src);
	};

	impl _get_instance_base_type() -> string-name {
		return _gdu_intern("ScriptExtension");
	};
	impl _has_source_code() -> bool   { return true; };
	impl _get_source_code() -> string {
		auto d = gdu_string_dup(&me -> src);
		return d;
	};
	impl _set_source_code(string s) {
		_t(string).dtor(&me -> src);
		_t(string).copy(&me -> src, (void const*[]) {&s});
	};
};

class JanetScriptImage extends JanetScript {
	impl _has_source_code() -> bool { return false; };
	var struct gdjn_class_JanetScript_image {
		size_t   sz;
		uint8_t* buf;
	}: image;
	impl _get_instance_base_type() -> string-name {
		return _gdu_intern("ScriptExtension");
	};
};
