#include "texture/texture.h"
#include "sokol_gfx.h"
#include <d3d11.h>

#include "render/windows/pixel_format_convert.h"
#include "texture/rw_texture2d.h"

// 处理 D3D11 错误代码
void handle_d3d_error(HRESULT hr, const char* operation) {
	if (FAILED(hr)) {
		std::string error_msg = std::string(operation) + ": ";
		switch (hr) {
			case E_INVALIDARG:
				// 无效参数
				error_msg += "Invalid arguments";
			break;
			case E_OUTOFMEMORY:
				// 内存不足
				error_msg += "Out of memory";
			break;
			case DXGI_ERROR_DEVICE_REMOVED:
				// 设备移除
				error_msg += "Device removed";
			break;
			case DXGI_ERROR_DEVICE_RESET:
				// 设备重置
				error_msg += "Device reset";
			break;
			default:
				// 未知错误
				error_msg += "Unknown error (0x" + std::to_string(hr) + ")";
			break;
		}
		throw std::runtime_error(error_msg);
	}
}

void rw_texture2d::create(const texture_data& in_data) {
	sg_image_desc desc{};
	desc.type = SG_IMAGETYPE_2D;
	desc.render_target = false;
	desc.width = in_data.size.x();
	desc.height = in_data.size.y();
	desc.num_slices = 1;
	desc.num_mipmaps = 1;
	desc.usage = _SG_USAGE_DEFAULT;
	desc.pixel_format = in_data.pixel_format;
	desc.sample_count = 1;

	auto d3d_device = (ID3D11Device*)sg_d3d11_device();
	auto d3d_context = (ID3D11DeviceContext*)sg_d3d11_device_context();

	// 创建纹理
	ID3D11Texture2D* tex2d = nullptr;
	D3D11_TEXTURE2D_DESC tex_desc{};
	tex_desc.Width = desc.width;
	tex_desc.Height = desc.height;
	tex_desc.MipLevels = 1;
	tex_desc.ArraySize = 1;
	tex_desc.Format = sg_pixel_format_to_dxgi(desc.pixel_format);
	tex_desc.SampleDesc.Count = 1;
	tex_desc.SampleDesc.Quality = 0;
	tex_desc.Usage = D3D11_USAGE_DEFAULT;
	tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	tex_desc.CPUAccessFlags = 0;
	tex_desc.MiscFlags = 0;

	auto hr = d3d_device->CreateTexture2D(&tex_desc, nullptr, &tex2d);
	handle_d3d_error(hr, "Failed to create texture");

	// 创建视图
	ID3D11ShaderResourceView* srv = nullptr;
	D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc{};
	srv_desc.Format = tex_desc.Format;
	srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srv_desc.Texture2D.MostDetailedMip = 0;
	srv_desc.Texture2D.MipLevels = 1;

	hr = d3d_device->CreateShaderResourceView(tex2d, &srv_desc, &srv);
	handle_d3d_error(hr, "Failed to create shader resource view");

	// 设置纹理
	desc.d3d11_texture = tex2d;
	desc.d3d11_shader_resource_view = srv;

	create_from_desc(desc);

	const auto& image_data = in_data.get_image_data();
	if (image_data.subimage[0][0].ptr)
		update(image_data);
}

void rw_texture2d::sub_update(const sg_range& in_data, const rect_t<int32_t>& in_rect, uint32_t src_row_pitch) {
	if (!is_valid()) {
		// 无法更新无效纹理
		throw std::runtime_error("Cannot update invalid texture");
	}

	// 获取 D3D11 资源
	const auto& image_info = sg_d3d11_query_image_info(get_image());
	const auto& context    = (ID3D11DeviceContext*)sg_d3d11_device_context();
	const auto& tex2d      = (ID3D11Texture2D*)image_info.tex2d;

	if (!tex2d || !context) {
		// 无法获取 D3D11 纹理或上下文
		throw std::runtime_error("Failed to get D3D11 texture or context");
	}

	// 获取纹理描述
	D3D11_TEXTURE2D_DESC desc;
	tex2d->GetDesc(&desc);

	// 边界检查
	if (in_rect.left() < 0 || in_rect.top() < 0 ||
		in_rect.right() > (int32_t)desc.Width ||
		in_rect.bottom() > (int32_t)desc.Height) {
		throw std::runtime_error("Update region out of texture bounds");
	}

	// 计算或使用提供的行间距
	uint32_t row_pitch = src_row_pitch;
	if (row_pitch == 0) {
		row_pitch = in_rect.width() * get_bytes_per_pixel();
	}

	// 检查数据大小是否足够
	if (in_data.size < (size_t)(in_rect.height() * row_pitch)) {
		throw std::runtime_error("Insufficient data for update region");
	}

	// 使用UpdateSubresource更新纹理
	D3D11_BOX box;
	box.left   = in_rect.left();
	box.top    = in_rect.top();
	box.front  = 0;
	box.right  = in_rect.right();
	box.bottom = in_rect.bottom();
	box.back   = 1;

	context->UpdateSubresource(tex2d, 0, &box, in_data.ptr, in_rect.width() * get_bytes_per_pixel(), 0);
}

std::vector<std::byte> rw_texture2d::read() const {
	// 获取纹理大小
	const auto& size = get_size();
	if (size.x() <= 0 || size.y() <= 0) {
		// 无效的纹理大小
		throw std::runtime_error("Invalid texture size");
	}

	auto texture_info = sg_d3d11_query_image_info(get_image());
	auto tex2d = (ID3D11Texture2D*)texture_info.tex2d;
	auto d3d_device = (ID3D11Device*)sg_d3d11_device();
	auto d3d_context = (ID3D11DeviceContext*)sg_d3d11_device_context();

	// 获取纹理描述
	D3D11_TEXTURE2D_DESC desc{};
	tex2d->GetDesc(&desc);

	// 创建STAGING纹理
	D3D11_TEXTURE2D_DESC staging_desc = desc;
	staging_desc.Usage = D3D11_USAGE_STAGING;

	ID3D11Texture2D* staging_tex = nullptr;
	auto hr = d3d_device->CreateTexture2D(&staging_desc, nullptr, &staging_tex);
	handle_d3d_error(hr, "Failed to create staging texture");

	// 使用智能指针确保纹理始终被正确释放
	std::shared_ptr<ID3D11Texture2D> shared_staging_tex(staging_tex, [](ID3D11Texture2D* ptr) { ptr->Release(); });

	// 复制纹理数据
	D3D11_BOX box{};
	box.left = 0;
	box.top = 0;
	box.front = 0;
	box.right = size.x();
	box.bottom = size.y();
	box.back = 1;

	d3d_context->CopySubresourceRegion(staging_tex, 0, 0, 0, 0, tex2d, 0, &box);

	// 映射STAGING纹理
	D3D11_MAPPED_SUBRESOURCE mapped{};
	hr = d3d_context->Map(staging_tex, 0, D3D11_MAP_READ, 0, &mapped);
	handle_d3d_error(hr, "Failed to map staging texture");

	// 复制数据
	std::vector<std::byte> data(size.x() * size.y() * get_bytes_per_pixel());
	std::memcpy(data.data(), mapped.pData, data.size());

	// 解除映射
	d3d_context->Unmap(staging_tex, 0);

	return data;
}

