#include <psemek/wgpu/shader_module.hpp>
#include <psemek/wgpu/detail/string_view.hpp>
#include <psemek/wgpu/external/webgpu.h>
#include <psemek/wgpu/external/wgpu.h>

#include <memory>

namespace psemek::wgpu
{

	void shader_module::get_compilation_info(callback_mode mode, compilation_info_callback const & callback) const
	{
		WGPUCompilationInfoCallbackInfo callback_info = {};
		callback_info.mode = (WGPUCallbackMode)mode;
		callback_info.callback = [](WGPUCompilationInfoRequestStatus status, WGPUCompilationInfo const * info, void * userdata, void *)
		{
			std::unique_ptr<compilation_info_callback> callback((compilation_info_callback *)userdata);

			// TODO support chained struct
			compilation_info cinfo;
			if (info)
			{
				for (std::size_t i = 0; i < info->messageCount; ++i)
				{
					auto & message = cinfo.messages.emplace_back();
					auto & message_in = info->messages[i];

					message.message.assign(message_in.message.data, message_in.message.length);
					message.type = (compilation_message_type)message_in.type;
					message.line_num = message_in.lineNum;
					message.line_pos = message_in.linePos;
					message.offset = message_in.offset;
					message.length = message_in.length;
				}
			}

			if (*callback) (*callback)((compilation_info_request_status)status, cinfo);
		};
		callback_info.userdata1 = new compilation_info_callback(callback);

		wgpuShaderModuleGetCompilationInfo((WGPUShaderModule)get(), callback_info);
	}

	void shader_module::set_label(std::string const & label)
	{
		wgpuShaderModuleSetLabel((WGPUShaderModule)get(), detail::to_string_view(label));
	}

	void shader_module::reference(void * ptr)
	{
		wgpuShaderModuleAddRef((WGPUShaderModule)ptr);
	}

	void shader_module::release(void * ptr)
	{
		wgpuShaderModuleRelease((WGPUShaderModule)ptr);
	}

	detail::chained_struct_ptr to_chained_struct(shader_module::spirv_descriptor const & value)
	{
		WGPUShaderSourceSPIRV chained = {};
		chained.chain.sType = WGPUSType_ShaderSourceSPIRV;
		chained.codeSize = value.code.size(); // TODO: is it in bytes or in words?
		chained.code = value.code.data();
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(shader_module::wgsl_descriptor const & value)
	{
		WGPUShaderSourceWGSL chained = {};
		chained.chain.sType = WGPUSType_ShaderSourceWGSL;
		chained.code = detail::to_string_view(value.code);
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(shader_module::glsl_descriptor && value)
	{
		struct chained_storage
		{
			std::vector<WGPUShaderDefine> defines;
		};

		chained_storage storage;
		storage.defines.resize(value.defines.size());
		for (int i = 0; i < value.defines.size(); ++i)
		{
			storage.defines[i].name = detail::to_string_view(value.defines[i].name);
			storage.defines[i].value = detail::to_string_view(value.defines[i].value);
		}

		WGPUShaderSourceGLSL chained = {};
		chained.chain.sType = (WGPUSType)WGPUSType_ShaderSourceGLSL;
		chained.stage = (WGPUShaderStage)value.stage;
		chained.code = detail::to_string_view(value.code);
		chained.defineCount = storage.defines.size();
		chained.defines = storage.defines.data();
		return detail::make_chained_struct(chained, std::move(storage));
	}

}
