import aliases;
import std.file;
import std.stdio;
import util;
import std.exception;
import alloc;

File Asset_File;

FileHeader Asset_Header;

AssetInfo[] Asset_Info;

u8[][] Asset_Data;

const u32 FILE_VERSION = 2;

enum AssetType : u32
{
	None,
	ModelM3D,
	Shader,
	Texture,
}

alias AT = AssetType;

struct FileHeader
{
	u32 magic;
	u32 file_version;
	u64 asset_count;
	u64 asset_info_offset;
}

struct ModelMeta
{
	u64 index_count;
}

struct TexData
{
	void* data;
	TexMeta meta;
}

struct TexMeta
{
	u32 w;
	u32 h;
	u32 ch;
}

struct AssetInfo
{
	u64 hash;
	u64 offset;
	u64 length;
	AssetType type;
}

bool Asset_Pack_Opened = false;

void
OpenAssetPack()
{
	if (!Asset_Pack_Opened)
	{
		bool success = true;
		string file_path = exists("build/assets.sgp") ? "build/assets.sgp" : "assets.sgp";

		// TODO: replace this with something that doesn't throw an exception and figure out if this is the best way to handle thing (probably isnt)
		try 
		{
			Asset_File = File(file_path, "rb");
		}
		catch (ErrnoException e)
		{
			Logf("OpenAssetPack failure: Unable to open file %s", file_path);
			assert(false, "Unable to open asset pack file");
		}

		FileHeader[1] header_arr;

		Asset_File.rawRead(header_arr);

		Asset_Header = header_arr[0];

		Asset_Info = AllocArray!(AssetInfo)(Asset_Header.asset_count);
		Asset_Data = AllocArray!(u8[])(Asset_Header.asset_count);

		assert(Asset_Header.file_version == FILE_VERSION, "OpenAssetPack failure: file version incorrect");

		Asset_File.seek(Asset_Header.asset_info_offset);

		Asset_File.rawRead(Asset_Info);
	}
}

pragma(inline): void
CheckAssetPack()
{
	if (!Asset_Pack_Opened)
	{
		OpenAssetPack();
	}
}

AssetInfo
GetAssetInfo(string name)
{
	CheckAssetPack();

	u64 hash = Hash(name);

	AssetInfo asset_info;
	foreach(i, info; Asset_Info)
	{
		if (info.hash == hash)
		{
			asset_info = info;
			break;
		}
	}

	assert(asset_info.hash != 0, "GetAssetInfo failure: unable to find matching asset");

	return asset_info;
}

u8[]
LoadAssetData(Arena* arena, string name)
{
	CheckAssetPack();

	u64 hash = Hash(name);
	u8[] data = null;

	foreach(i, info; Asset_Info)
	{
		if (info.hash == hash)
		{
			data = AllocArray!(u8)(arena, info.length);
			Asset_File.seek(info.offset);
			Asset_File.rawRead(data);
			assert(data != null && data.length == info.length, "LoadAssetData failure: Asset data loaded incorrectly");

			break;
		}
	}

	return data;
}

u8[]
LoadAssetData(string name)
{
	CheckAssetPack();

	u64 hash = Hash(name);
	u8[] data = null;

	foreach(i, info; Asset_Info)
	{
		if (info.hash == hash)
		{
			if (Asset_Data[i].ptr == null)
			{
				Asset_Data[i] = AllocArray!(u8)(info.length);
				Asset_File.seek(info.offset);
				Asset_File.rawRead(Asset_Data[i]);
				assert(Asset_Data[i] != null && Asset_Data[i].length == info.length, "LoadAssetData failure: Asset data loaded incorrectly.");
			}

			data = Asset_Data[i];
			break;
		}
	}

	return data;
}

void
UnloadAssetData(string name)
{
	u64 hash = Hash(name);

	foreach(i, info; Asset_Info)
	{
		if (info.hash == hash)
		{
			if (Asset_Data[i] != null)
			{
				FreeArray(Asset_Data[i]);
				break;
			}
		}
	}
}



