#include "codegen.h"

#include "os.cpp"

#include "xxhash/xxhash.c"

u64
WriteArrayToFile(Arena *arena, pFile file, String8 array_name, rawptr elements, u32 count, String8 type, u64 offset)
{
	b32 is_string = StrEq(type.value, "c8 *");
	c8 *decl = MakeArray(arena, c8, 2048);
	i32 decl_len = SPrintf(decl, 2048, "static %s\n%s[] = \n{\n\t", type.value, array_name.value);
	String8 result = {
		.value = decl,
		.len = u64(decl_len),
	};

	if (is_string)
	{
		String8 *strs = (String8 *)elements;
		for (u32 i = 0; i < count; i += 1)
		{
			const c8 *f = i == count-1 ? "\"%s\"," : "\"%s\",\n\t";
			c8 buf[512];
			decl_len = SPrintf(buf, 512, f, strs[i].value);
			result = String8Concat(arena, result, MakeString8(buf, decl_len));
		}
	}
	else
	{
		u64 *ints = (u64 *)elements;
		for (u32 i = 0; i < count; i += 1)
		{
			const c8 *f = i == count-1 ? "%lluU," : "%lluU,\n\t";
			c8 buf[512];
			decl_len = SPrintf(buf, 512, f, ints[i]);
			result = String8Concat(arena, result, MakeString8(buf, decl_len));
		}
	}

	result = String8Concat(arena, result, String8CStr("\n};\n\n"));

	return pFileWrite(file, offset, result.value, result.len);
}

static String8 *
ConvertCStrs(Arena *arena, c8 **strs, u32 count)
{
	String8 *str8s = MakeArray(arena, String8, count);

	for (u32 i = 0; i < count; i += 1)
	{
		str8s[i].value = strs[i];
		str8s[i].len = StrLen(strs[i]);
	}

	return str8s;
}

void
CodeGenAssetLookups(Arena *arena)
{
	pFile file = pFileOpen("../src/codegen_assets.h", pFS_WRITE | pFS_CREATE | pFS_TRUNC);
	Assert(file > 0, "CodeGenAssetLookups failure: unable to open/create file");

	Assert(pDirNavigate("../assets") == 0, "Unable to navigate to asset directory");

	struct AssetDirInfo {
		c8 *prefix, *struct_name, *hash_struct_name, *dir, *define;
	};

	struct AssetDirInfo dirs[] = {
		{
			.prefix = "shaders/",
			.struct_name = "g_Shader_Asset_Names",
			.hash_struct_name = "g_Shader_Asset_Hashes",
			.dir = "shaders",
			.define = "SHADER_ASSET",
		},
		{
			.prefix = "models/",
			.struct_name = "g_Model_Asset_Names",
			.hash_struct_name = "g_Model_Asset_Hashes",
			.dir = "models",
			.define = "MODEL_ASSET",
		},
		{
			.prefix = "textures/",
			.struct_name = "g_Texture_Asset_Names",
			.hash_struct_name = "g_Texture_Asset_Hashes",
			.dir = "textures",
			.define = "TEXTURE_ASSET",
		},
	};

	u64 total_assets = 0;
	u64 offset = 0;
	u32 dir_count = sizeof(dirs) / sizeof(struct AssetDirInfo);
	for (u32 i = 0; i < dir_count; i += 1)
	{
		Assert(pDirNavigate(dirs[i].dir) == 0, "Unable to navigate to asset sub directory.");

		u32 count = 0;
		c8 **cstrs = pDirGetFileNames(arena, &count);
		u64 *hashes = MakeArray(arena, u64, count);
		String8 *strs = ConvertCStrs(arena, cstrs, count);

		for (u32 j = 0; j < count; j += 1)
		{
			strs[j] = String8Concat(arena, String8CStr(dirs[i].prefix), strs[j]);
			i64 offset = String8FindLast(strs[j], '.');
			hashes[j] = XXH3_64bits_withSeed(strs[j].value, offset, HASH_SEED);
		}

		offset += WriteArrayToFile(
			arena, 
			file, 
			String8CStr(dirs[i].struct_name), 
			strs,
			count,
			String8CStr("c8 *"),
			offset
		);

		offset += WriteArrayToFile(
			arena,
			file,
			String8CStr(dirs[i].hash_struct_name),
			hashes,
			count,
			String8CStr("u64"),
			offset
		);

		c8 buf[256];
		i32 decl_len = SPrintf(buf, 256, "#define %s_MAX %lluU\n\n#define %s %dU\n\n", dirs[i].define, count, dirs[i].define, i);

		offset += pFileWrite(file, offset, buf, decl_len);

		total_assets += count;

		if (i == dir_count-1)
		{
			decl_len = SPrintf(buf, 256, "#define ASSET_MAX %lluU\n\n#define RENDERER_ASSET_MAX (ASSET_MAX - SHADER_ASSET_MAX)\n\n#define ASSET_TYPE_MAX %dU\n\n", total_assets, i+1);
			pFileWrite(file, offset, buf, decl_len);
		}

		Assert(pDirNavigate("..") == 0, "CodeGenAssetLookups failure: unable to move back to build directory");
	}

	pFileClose(file);
}

int
main(int argc, c8 **argv)
{
	#ifdef _WIN32
	{
		_set_fmode(_O_BINARY);
	}
	#endif

	if (pDirIsVisible("build"))
	{
		Assert(pDirNavigate("./build") == 0, "Unable to change to build directory");
	}

	void *mem = pMemAllocZeroed(GB(1));
	Arena *arena = ArenaInitDebug(mem, GB(1), __LINE__);

	CodeGenAssetLookups(arena);
}
