import aliases;
import includes;
import assets;
import util;
import ap = assets;
import vk = vulkan;
import u = util;
import p = platform;

alias Shader = VkShaderModule;
alias Pipeline = vk.PipelineHandle;
alias Attribute = VkVertexInputAttributeDescription;

enum InputRate : int
{
	Vertex = VK_VERTEX_INPUT_RATE_VERTEX,
	Instance = VK_VERTEX_INPUT_RATE_INSTANCE,
}

enum Format: int
{
	UINT = VK_FORMAT_R32_UINT,
	R_F32 = VK_FORMAT_R32_SFLOAT,
	RG_F32 = VK_FORMAT_R32G32_SFLOAT,
	RGB_F32 = VK_FORMAT_R32G32B32_SFLOAT,
	RGBA_F32 = VK_FORMAT_R32G32B32A32_SFLOAT,
}

enum PipelineType : int
{
	Graphics,
	Compute,
}

alias PT = PipelineType;

struct GfxPipelineInfo
{
	Shader vertex_shader;
	Shader frag_shader;
	InputRate input_rate;
	u32 input_rate_stride;
	Attribute[] vertex_attributes;
}

struct Renderer
{
	vk.Vulkan vulkan;
	p.Window* window;
	Pipeline triangle_pipeline;
	Pipeline compute_pipeline;
}

Renderer
Init(p.Window* window)
{
	u.Result!(vk.Vulkan) vk_result = vk.Init(window, u.MB(24), u.MB(32));
	assert(vk_result.ok, "Init failure: Unable to initialize Vulkan");

	Renderer rd = {
		vulkan: vk_result.value,
		window: window,
	};

	rd.triangle_pipeline = BuildGfxPipeline(&rd, "shaders/triangle.vert", "shaders/triangle.frag");
	rd.compute_pipeline = BuildCompPipeline(&rd, "shaders/gradient.comp");

	return rd;
}

bool
Cycle(Renderer* rd)
{
	bool success = vk.BeginFrame(&rd.vulkan);

	if (success)
	{
		vk.Bind(&rd.vulkan, rd.compute_pipeline);

		vk.PrepCompute(&rd.vulkan);

		vk.Dispatch(&rd.vulkan);

		vk.BeginRender(&rd.vulkan);

		vk.Bind(&rd.vulkan, rd.triangle_pipeline);

		vk.Draw(&rd.vulkan, 3, 1);

		success = vk.FinishFrame(&rd.vulkan);
	}

	return success;
}

Pipeline
BuildGfxPipeline(Renderer* rd, string vertex, string fragment)
{
	u8[] vert_bytes = ap.LoadAssetData(vertex);
	u8[] frag_bytes = ap.LoadAssetData(fragment);
	
	scope(exit) 
	{
		ap.UnloadAssetData(vertex);
		ap.UnloadAssetData(fragment);
	}

	assert(vert_bytes && frag_bytes, "Unable to load shaders");

	Result!(Shader) vert_module = vk.BuildShader(&rd.vulkan, vert_bytes);
	Result!(Shader) frag_module = vk.BuildShader(&rd.vulkan, frag_bytes);

	assert(vert_module.ok && frag_module.ok, "Unable to build vulkan shaders");

	scope(exit)
	{
		vk.Destroy(&rd.vulkan, vert_module.value);
		vk.Destroy(&rd.vulkan, frag_module.value);
	}

	GfxPipelineInfo pipeline_info = {
		vertex_shader: vert_module.value,
		frag_shader: frag_module.value,
	};

	return vk.CreateGraphicsPipeline(&rd.vulkan, &pipeline_info);
}

Pipeline
BuildCompPipeline(Renderer* rd, string compute)
{
	u8[] comp_bytes = ap.LoadAssetData(compute);
	assert(comp_bytes != null, "Unable to load compute shader data");
	scope(exit) ap.UnloadAssetData(compute);

	Result!(Shader) comp_module = vk.BuildShader(&rd.vulkan, comp_bytes);
	assert(comp_module.ok, "Unable to build compute shader");
	scope(exit) vk.Destroy(&rd.vulkan, comp_module.value);

	return vk.CreateComputePipeline(&rd.vulkan, comp_module.value);
}

void
Destroy(Renderer* rd)
{
	vk.Destroy(&rd.vulkan, rd.triangle_pipeline);
	vk.Destroy(&rd.vulkan, rd.compute_pipeline);
	vk.Destroy(&rd.vulkan);
}
