// ::Packer::Includes::CFiles::Start::

#include "os.cpp"

#include "packer.h"

#include "assets.cpp"


#ifdef _WIN32
# include <fcntl.h>
#endif

// ::Packer::Packing::Functions::Start::

i32 
WriteHeader(pFile file, FileHeader *header)
{
	i32 offset = 0;
	
	return offset;
}

void 
MoveToShaderDir(c8 **return_dir)
{
	Assert(pDirNavigate("../assets/shaders") == 0, "Unable to change to shader directory");
	*return_dir = "../../build";
}

void 
MoveToTextureDir(c8 **return_dir)
{
	Assert(pDirNavigate("../assets/textures") == 0, "Unable to change to assets directory");
	*return_dir = "../../build";
}

void 
MoveToModelDir(c8 **return_dir)
{
	Assert(pDirNavigate("../assets/models") == 0, "Unable to change to assets directory");
	*return_dir = "../../build";
}

void 
InitHeader(FileHeader *header)
{
	Assert(header != NULL, "File header is null");

	header->magic_num = CreateMagicValue('s', 't', 'e', 'g');
	header->version = FILE_VERSION;

	header->asset_counts = SHADER_ASSET_MAX + TEXTURE_ASSET_MAX + MODEL_ASSET_MAX;
	header->asset_offset = sizeof(FileHeader);
}

void 
PackFiles(Arena *arena, FileHeader *header)
{
	pFile file = pFileOpen("assets.sgp", pFS_WRITE | pFS_TRUNC | pFS_CREATE);

	u64 file_pos = pFileWrite(file, 0, header, sizeof(FileHeader));

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

	u64 total_assets = SHADER_ASSET_MAX + TEXTURE_ASSET_MAX + MODEL_ASSET_MAX;
	u64 asset_count = 0;
	AssetFile *assets = MakeArray(arena, AssetFile, total_assets);

	u64 data_offset = header->asset_offset + (sizeof(AssetFile) * total_assets);

	for (u32 i = 0; i < SHADER_ASSET_MAX; i++, asset_count++)
	{
		c8 *asset_name = g_Shader_Asset_Names[i];

		Printfln("Packing file: %s at offset %llu...", asset_name, data_offset);

		pFile asset_file = pFileOpen(asset_name, pFS_READ);
		u64 file_size = pFileLength(asset_file);

		u8 *file_data = MakeArray(arena, u8, file_size);
		pFileRead(asset_file, 0, file_data, file_size);

		u64 prev_offset = data_offset;
		data_offset += pFileWrite(file, prev_offset, file_data, file_size);

		Assert((data_offset - prev_offset) == file_size, "File write size invalid");

		assets[asset_count].data_offset = prev_offset;
		assets[asset_count].len = file_size;
		assets[asset_count].type = AT_SHADER;
		assets[asset_count].hash = g_Shader_Asset_Hashes[i];

		pFileClose(asset_file);
	}

	for (u32 i = 0; i < TEXTURE_ASSET_MAX; i++, asset_count++)
	{
		c8 *asset_name = g_Texture_Asset_Names[i];

		Printfln("Packing file: %s at offset %llu...", asset_name, data_offset);

		pFile asset_file = pFileOpen(asset_name, pFS_READ);
		u64 file_size = pFileLength(asset_file);

		u8 *file_data = MakeArray(arena, u8, file_size);
		pFileRead(asset_file, 0, file_data, file_size);

		int ch = 4;
		int w, h, has_ch;
		u8 *image_bytes = stbi_load_from_memory(file_data, file_size, &w, &h, &has_ch, ch);
		if (w <= 0 || h <= 0 || has_ch <= 0)
		{
			Printfln("%s", stbi_failure_reason());
			Assert(0, "stbi_load_from_memory failure");
		}

		u64 loaded_length = u64(w * h * ch);
		
		u64 prev_offset = data_offset;
		data_offset += pFileWrite(file, data_offset, image_bytes, loaded_length);

		Assert((data_offset - prev_offset) == loaded_length, "File write size invalid");

		assets[asset_count].data_offset = prev_offset;
		assets[asset_count].len = loaded_length;
		assets[asset_count].type = AT_TEXTURE;
		assets[asset_count].hash = g_Texture_Asset_Hashes[i];
		assets[asset_count].texture_meta.w = u32(w);
		assets[asset_count].texture_meta.h = u32(h);
		assets[asset_count].texture_meta.ch = u32(ch);

		stbi_image_free(image_bytes);

		pFileClose(asset_file);

	}

	for (u32 i = 0; i < MODEL_ASSET_MAX; i++, asset_count++)
	{
		c8 *asset_name = g_Model_Asset_Names[i];

		Printfln("Packing file: %s at offset %llu...", asset_name, data_offset);

		pFile asset_file = pFileOpen(asset_name, pFS_READ);
		u64 file_size = pFileLength(asset_file);

		u8 *file_data = MakeArray(arena, u8, file_size);
		pFileRead(asset_file, 0, file_data, file_size);

		u64 prev_offset = data_offset;
		data_offset += pFileWrite(file, data_offset, file_data, file_size);

		Assert((data_offset - prev_offset) == file_size, "File write size invalid");

		m3d_t *model = m3d_load(file_data, NULL, NULL, NULL);

		assets[asset_count].data_offset = prev_offset;
		assets[asset_count].type = AT_MODEL;
		assets[asset_count].len = file_size;
		assets[asset_count].hash = g_Model_Asset_Hashes[i];
		assets[asset_count].model_meta.i_count = u64(model->numface * 3);

		m3d_free(model);

		pFileClose(asset_file);
	}

	pFileWrite(file, header->asset_offset, assets, sizeof(AssetFile)*asset_count);

	pFileClose(file);

	Assert(pDirNavigate("../build") == 0, "Unable to navigate back to build directory");
}

// ::Packer::Packing::Functions::End::



// ::Packer::Tests::Functions::Start::

static inline void 
TestAssetIsCorrect(Arena *arena, c8 *file_name, AssetFile *file_info, pFile file)
{
	pFile asset_file = pFileOpen(file_name, pFS_READ);
	u64 size = pFileLength(asset_file);

	u8 *file_data = MakeArray(arena, u8, size);
	u64 write_count = pFileRead(asset_file, 0, file_data, size);
	Assert(write_count == size, "Incorrect asset size retrieved");

	u8 *packed_asset = MakeArray(arena, u8, file_info->len);
	pFileRead(file, file_info->data_offset, packed_asset, file_info->len);

	u8 *image_bytes;
	u64 image_length;
	if (file_info->type == TEXTURE_ASSET)
	{
		int ch = 4;
		int w, h, has_ch;
		image_bytes = stbi_load_from_memory(file_data, size, &w, &h, &has_ch, ch);
		image_length = u64(w * h * ch);

		Assert(file_info->len == image_length, "file length incorrect");

		for (u64 i = 0; i < image_length; i++)
		{
			Assert(image_bytes[i] == packed_asset[i], "Asset file data is invalid");
		}

		stbi_image_free(image_bytes);
	}
	else 
	{
		Assert(file_info->len == size, "file length incorrect");

		for (u64 i = 0; i < size; i++)
		{
			Assert(file_data[i] == packed_asset[i], "Asset file data is invalid");
		}
	}

	pFileClose(asset_file);
}

void 
TestAssetPack(Arena *arena)
{
	pFile file = pFileOpen("assets.sgp", pFS_READ);

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

	FileHeader header;
	i64 offset = pFileRead(file, 0, &header, sizeof(FileHeader));

	u64 asset_count = SHADER_ASSET_MAX + MODEL_ASSET_MAX + TEXTURE_ASSET_MAX;

	Assert(header.magic_num == CreateMagicValue('s', 't', 'e', 'g'), "Magic number is incorrect");
	Assert(header.version == FILE_VERSION, "File version is incorrect");

	Assert(header.asset_counts == asset_count, "Asset count incorrect");

	AssetFile *files = MakeArray(arena, AssetFile, asset_count);
	pFileRead(file, header.asset_offset, files, sizeof(AssetFile)*header.asset_counts);
	
	u64 current_asset = 0;
	for (u32 i = 0; i < SHADER_ASSET_MAX; i++, current_asset++)
	{
		TestAssetIsCorrect(arena, g_Shader_Asset_Names[i], files + current_asset, file);
		Assert(files[current_asset].hash == g_Shader_Asset_Hashes[i], "Shader asset hash mismatch");
	}

	for (u32 i = 0; i < TEXTURE_ASSET_MAX; i++, current_asset++)
	{
		TestAssetIsCorrect(arena, g_Texture_Asset_Names[i], files + current_asset, file);
		Assert(files[current_asset].hash == g_Texture_Asset_Hashes[i], "Texture asset hash mismatch");
	}

	for (u32 i = 0; i < MODEL_ASSET_MAX; i++, current_asset++)
	{
		TestAssetIsCorrect(arena, g_Model_Asset_Names[i], files + current_asset, file);
		Assert(files[current_asset].hash == g_Model_Asset_Hashes[i], "Model asset hash mismatch");
	}
}

// ::Packer::Tests::Functions::End::



// ::Packer::Main::Functions::Start::

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__);

	FileHeader header = {0};
	InitHeader(&header);

	PackFiles(arena, &header);

	TestAssetPack(arena);
}



// ::Packer::Main::Functions::End::
