#include <stdio.h>
#include <stdbool.h>
#define DESKTOP
#include "better_assert.h"

#include "buff.h"

#pragma warning(disable : 4996) // nonsense about fopen being insecure

#if defined(__clang__)
#define no_ubsan __attribute__((no_sanitize("undefined")))
#else
#define no_ubsan 
#endif

#pragma warning(push)
#pragma warning(disable : 4244) // loss of data warning
#pragma warning(disable : 4101) // unreferenced local variable
#define STBSP_ADD_TO_FUNCTIONS no_ubsan
#define MD_FUNCTION no_ubsan
#include "md.h"
#include "md.c"
#pragma warning(pop)


#define Log(...) { printf("Codegen: "); printf(__VA_ARGS__); }

void dump(MD_Node* from) {
	printf("/ %.*s\n", MD_S8VArg(from->string));
	int d = 0;
	for (MD_EachNode(child, from->first_child))
	{
		printf("|-- Child %d Tag [%.*s] string[%.*s] first child string[%.*s]\n", d, MD_S8VArg(child->first_tag->string), MD_S8VArg(child->string), MD_S8VArg(child->first_child->string));
		d += 1;
	}
}
bool has_decimal(MD_String8 s)
{
	for (int i = 0; i < s.size; i++)
	{
		if (s.str[i] == '.') return true;
	}
	return false;
}

MD_Arena *cg_arena = NULL;

#define S8(s) MD_S8Lit(s)
#define S8V(s) MD_S8VArg(s)

MD_String8 ChildValue(MD_Node *n, MD_String8 name) {
	MD_Node *child_with_value = MD_ChildFromString(n, name, 0);
	assert(child_with_value);
	assert(!MD_NodeIsNil(child_with_value->first_child)); // MD_S8Lit("Must have child"));
	return child_with_value->first_child->string;
}

MD_String8 asset_file_path(MD_String8 filename) {
	return MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(S8("assets")), MD_S8VArg(filename));
}

char *nullterm(MD_String8 s) {
	char *to_return = MD_ArenaPush(cg_arena, s.size + 1);
	memcpy(to_return, s.str, s.size);
	to_return[s.size] = '\0';
	return to_return;
}

char* fillnull(char *s, char c) {
	while (*s != '\0') {
		if (*s == c) {
			*s = '\0';
			return s + 1;
		}
		s++;
	}
	assert(false); // MD_S8Lit("Couldn't find char"));
	return NULL;
}

#define StrSame(s1, s2) MD_S8Match((s1), (s2), 0)
#define EachString(it, first) MD_String8Node *it = (first); it != 0; it = it->next

typedef BUFF(MD_Node*, 256) Nodes;
MD_Node* find_by_name(Nodes *n, MD_String8 name)
{
	MD_Node *node_with = 0;
	BUFF_ITER(MD_Node *, n)
	{
		if (StrSame((*it)->string, name))
		{
			assert(node_with == 0);
			node_with = (*it);
		}
	}
	assert(node_with);
	return node_with;
}

char* goto_end_of(char *tomove, size_t max_move, char *pattern) {
	size_t pattern_len = strlen(pattern);
	for (int i = 0; i < max_move; i++) {
		if (strncmp(tomove, pattern, pattern_len) == 0) {
			tomove += pattern_len;
			return tomove;
		}
		tomove++;
	}
	return NULL;
}

#define list_printf(list_ptr, ...) MD_S8ListPush(cg_arena, list_ptr, MD_S8Fmt(cg_arena, __VA_ARGS__))
void dump_full(MD_Node* from)
{
	for (MD_EachNode(node, from))
	{
		printf("@%.*s %.*s\n", MD_S8VArg(node->first_tag->string), MD_S8VArg(node->string));
	}
	/* MD_String8List output_list = {0};
		 MD_DebugDumpFromNode(cg_arena, &output_list, from, 4, S8("  "), 0);
		 MD_StringJoin join = (MD_StringJoin){0};
		 MD_String8 debugged = MD_S8ListJoin(cg_arena, output_list , &join);
		 printf("%.*s\n", MD_S8VArg(debugged));*/
}

#include "character_info.h"

int main(int argc, char **argv)
{
	cg_arena = MD_ArenaAlloc();
	assert(cg_arena);

	MD_String8 test = MD_S8Lit("*testing*other");
	MD_String8 to_split = MD_S8Lit("*");
	MD_String8List split_up = MD_S8Split(cg_arena, test, 1, &to_split);

	printf("Split up: ");
	for(MD_String8Node * cur = split_up.first; cur; cur = cur->next)
	{
		printf("'%.*s', ", MD_S8VArg(cur->string));
	}
	printf("\n");

	// do characters

	FILE *char_header = fopen("gen/characters.gen.h", "w");
	fprintf(char_header, top_of_header);

#define GEN_TABLE(arr_elem_type, table_name, arr, str_access) { fprintf(char_header, "char *%s[] = {\n", table_name); ARR_ITER(arr_elem_type, arr) fprintf(char_header, "\"%s\",\n", str_access); fprintf(char_header, "}; // %s\n", table_name); }
#define GEN_ENUM(arr_elem_type, arr, enum_type_name, enum_name_access, fmt_str) { fprintf(char_header, "typedef enum\n{\n"); ARR_ITER(arr_elem_type, arr) fprintf(char_header, fmt_str, enum_name_access); fprintf(char_header, "} %s;\n", enum_type_name); GEN_TABLE(arr_elem_type, enum_type_name "_names", arr, enum_name_access); }
	GEN_ENUM(ActionInfo, actions, "ActionKind", it->name, "ACT_%s,\n");
	GEN_ENUM(CharacterGen, characters, "NpcKind", it->enum_name, "NPC_%s,\n");

	fclose(char_header);

	// do assets

	MD_String8 writeto = MD_S8Fmt(cg_arena, "gen/assets.gen.c");
	Log("Writing to %.*s\n", MD_S8VArg(writeto));
	FILE *output = fopen(nullterm(writeto), "w");

	MD_ParseResult parse = MD_ParseWholeFile(cg_arena, MD_S8Lit("assets.mdesk"));

	MD_String8List declarations_list = { 0 };
	MD_String8List load_list = { 0 };
	MD_String8List level_decl_list = { 0 };
	MD_String8List tileset_decls = { 0 };
	for (MD_EachNode(node, parse.node->first_child)) {
		if (MD_S8Match(node->first_tag->string, MD_S8Lit("sound"), 0)) {
			MD_String8 variable_name = MD_S8Fmt(cg_arena, "sound_%.*s", MD_S8VArg(node->string));
			Log("New sound variable %.*s\n", MD_S8VArg(variable_name));
			MD_String8 filepath = ChildValue(node, MD_S8Lit("filepath"));
			filepath = asset_file_path(filepath);
			assert(filepath.str != 0); // MD_S8Fmt(cg_arena, "No filepath specified for sound '%.*s'", MD_S8VArg(node->string)));
			FILE *asset_file = fopen(nullterm(filepath), "r");
			assert(asset_file); //  MD_S8Fmt(cg_arena, "Could not open filepath %.*s for asset '%.*s'", MD_S8VArg(filepath), MD_S8VArg(node->string)));
			fclose(asset_file);

			MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "AudioSample %.*s = {0};\n", MD_S8VArg(variable_name)));
			MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_wav_audio(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath)));
		}
		if (MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) {
			MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string));
			//Log("New image variable %.*s\n", MD_S8VArg(variable_name));
			MD_String8 filepath = ChildValue(node, MD_S8Lit("filepath"));
			filepath = asset_file_path(filepath);
			assert(filepath.str != 0); // , MD_S8Fmt(cg_arena, "No filepath specified for image '%.*s'", MD_S8VArg(node->string)));
			FILE *asset_file = fopen(nullterm(filepath), "rb");
			assert(asset_file); // , MD_S8Fmt(cg_arena, "Could not open filepath %.*s for asset '%.*s'", MD_S8VArg(filepath), MD_S8VArg(node->string)));
			fclose(asset_file);

			MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name)));
			MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(MD_S8Lit(\"%.*s\"));\n", MD_S8VArg(variable_name), MD_S8VArg(filepath)));
		}
	}


	MD_StringJoin join = { 0 };
	MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join);
	MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join);
	fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}\n", MD_S8VArg(declarations), MD_S8VArg(loads));

	fclose(output);

	return 0;
}
