#include "vulkan_glsl_loader.h"
#include "vulkanapi/tool/glsl_to_spirv.h"
#include "vulkanapi/vulkanapi.h"
#include "asset/file_manager.h"
#include "render/asset/vertex.h"
#include "vkmeta_vertex_gen.inl"
#include <spirv_cross/spirv_reflect.hpp>
#include "asset/file_handle.h"
using namespace engineapi;
namespace vulkanapi {
	vk::ShaderStageFlagBits GetShaderType(string_view ext)
	{
		switch (string_hash(ext))
		{
		case string_hash(".vert"): return vk::ShaderStageFlagBits::eVertex;
		case string_hash(".geom"): return vk::ShaderStageFlagBits::eGeometry;
		case string_hash(".tese"): return vk::ShaderStageFlagBits::eTessellationEvaluation;
		case string_hash(".tesc"): return vk::ShaderStageFlagBits::eTessellationControl;
		case string_hash(".frag"): return vk::ShaderStageFlagBits::eFragment;
		case string_hash(".comp"): return vk::ShaderStageFlagBits::eCompute;
		default:                   return vk::ShaderStageFlagBits::eAll;
		}
	}
	void VulkanGlslLoader::Init()
	{
		REGISTER_META_TABLE(PosVertex);
		REGISTER_META_TABLE(TexVertex);
		REGISTER_META_TABLE(BoneVertex);
		ResourceManager::GetSingleton().RegisterLoader<VulkanGlslLoader>(".geom");
		ResourceManager::GetSingleton().RegisterLoader<VulkanGlslLoader>(".frag");
		ResourceManager::GetSingleton().RegisterLoader<VulkanGlslLoader>(".vert");
	}
	void VulkanGlslLoader::LoadShaderInfo(const engineapi::Guid& guid, const std::vector<uint32_t>& spirv)
	{
		spirv_cross::Compiler compiler(spirv);
		auto resources = compiler.get_shader_resources();
		for (auto& ub : resources.uniform_buffers)
		{
			auto type = compiler.get_type(ub.type_id);
			auto binding = compiler.get_decoration(ub.id, spv::Decoration::DecorationBinding);
			auto set = compiler.get_decoration(ub.id, spv::Decoration::DecorationDescriptorSet);
		}
	}
	ResourceBundle VulkanGlslLoader::LoadFile(PackagePath path, const MetaBundle& meta)
	{
		auto m = meta.FetchMeta<ShaderProgram>();
		auto program = m ? ResourceManager::GetSingleton().LoaderEmplaceResource<vkShaderProgram>(m->guid)
			: ResourceManager::GetSingleton().LoaderEmplaceResource<vkShaderProgram>();
		FileHandle handle(path + ".meta");
		if (!handle.Open(FILE_OP::READ, mFileFlag & FileFlag::File_Binary)) {
			return program;
		}
		if (mFileFlag & FileFlag::File_Binary) {
			vector<char> data = handle.ReadAll();
			std::vector<unsigned int> spirv(data.size() / 4);
			std::memcpy(spirv.data(), data.data(), data.size());
			program->Load(spirv);
			LoadShaderInfo(program->GetGuid(), spirv);
		}
		else {
			string glsl = handle.ReadAll<string>();
			auto shader_enum = GetShaderType(path.GetExtension());
			auto spirv = GlslToSpirv::spirv(glsl, shader_enum, path.GetFileName());
			if (spirv) {
				program->Load(*spirv);
				LoadShaderInfo(program->GetGuid(), *spirv);
			}
		}
		return program;
	}
	void vkShaderProgram::Load(const std::vector<unsigned int>& spirv)
	{
		RenderVulkanAPI* renderApi = RenderVulkanAPI::GetSingletonPtr();
		mPtr = renderApi->backend.GetDevice().CreateShaderModule(spirv);
	}
}

