#include  "MeshPrimitives.h"
#include "Geometry.h"


namespace XEngine::tools
{
using namespace math;
using namespace DirectX;

namespace {
using primitive_mesh_creator = void(*)(scene&, const primitive_init_info& info);

void create_plane(scene& scene, const primitive_init_info& info);
void create_cube(scene& scene, const primitive_init_info& info);
void create_uv_sphere(scene& scene, const primitive_init_info& info);
void create_ico_sphere(scene& scene, const primitive_init_info& info);
void create_cylinder(scene& scene, const primitive_init_info& info);
void create_capsule(scene& scene, const primitive_init_info& info);

primitive_mesh_creator creators[]
{
	create_plane,
	create_cube,
	create_uv_sphere,
	create_ico_sphere,
	create_cylinder,
	create_capsule,
};

struct axis
{
	enum : u32 {
		x = 0,
		y = 1,
		z = 2,
	};
};


/**
 * @brief 创建一个平面网格（mesh）对象。
 *
 * 该平面网格可以通过参数定制其尺寸、分段、方向、UV 坐标范围以及顶点缠绕顺序。
 * 需要注意，position，uvs记录的是顶点，在后面的indices，uv_set中细节记录点和uv的分配。
 *
 * @param info 包含平面尺寸和分段信息的结构体。
 * @param horizontal_index 水平方向的轴索引 (0: x, 1: y, 2: z)，默认为 axis::x。
 * @param vertical_index 垂直方向的轴索引 (0: x, 1: y, 2: z)，默认为 axis::z。
 * @param flip_winding 是否翻转顶点缠绕顺序，默认为 false。
 * @param offset 平面的初始位置偏移，默认为 {-0.5f, 0.f, -0.5f}。
 * @param u_range U 坐标的范围，默认为 {0.f, 1.f}。
 * @param v_range V 坐标的范围，默认为 {0.f, 1.f}。
 * @return 创建的平面网格对象。
 */
mesh
create_plane(const primitive_init_info& info,
	u32 horizontal_index = axis::x, u32 vertical_index = axis::z, bool flip_winding = false,
	v3 offset = { -0.5f,0.f,-0.5f }, v2 u_range = { 0.f,1.f }, v2 v_range = { 0.f, 1.f })
{
	// 参数合法性检查
	assert(horizontal_index < 3 && vertical_index < 3);
	assert(horizontal_index != vertical_index);

	// 计算水平和垂直方向的分段数，并限制在合理范围内
	const u32 horizontal_count{ clamp(info.segments[horizontal_index], 1u ,10u) };
	const u32 vertical_count{ clamp(info.segments[vertical_index], 1u ,10u) };

	// 计算水平和垂直方向的步长
	const f32 horizontal_step{ 1.f / horizontal_count };
	const f32 vertical_step{ 1.f / vertical_count };

	// 计算 UV 坐标的步长
	const f32 u_step{ (u_range.y - u_range.x) / horizontal_count };
	const f32 v_step{ (v_range.y - v_range.x) / vertical_count };

	// 创建网格对象和 UV 坐标向量
	mesh m{};
	m.name = "plane";

	utl::vector<v2> uvs;

	// 生成顶点和 UV 坐标
	for (u32 j{ 0 }; j <= vertical_count; ++j)
		for (u32 i{ 0 }; i <= horizontal_count; ++i)
		{
			// 计算顶点位置
			v3 position{ offset };
			f32* const as_array{ &position.x };
			as_array[horizontal_index] += i * horizontal_step;
			as_array[vertical_index] += j * vertical_step;

			// 添加顶点位置到网格对象
			m.positions.emplace_back(position.x * info.size.x, position.y * info.size.y, position.z * info.size.z);

			// 计算 UV 坐标
			v2 uv{ u_range.x, 1.f - v_range.x };
			uv.x += i * u_step;
			uv.y -= j * v_step;

			// 添加 UV 坐标到向量
			uvs.emplace_back(uv);
		}

	// 顶点数量检查
	assert(m.positions.size() == (((u64)horizontal_count + 1) * ((u64)vertical_count + 1)));

	// 计算每行顶点数
	const u32 row_length{ horizontal_count + 1 };

	// 生成三角形索引
	for (u32 j{ 0 }; j < vertical_count; ++j)
	{
		for (u32 i{ 0 }; i < horizontal_count; ++i)
		{
			// 计算四边形四个顶点的索引
			const u32 index[4]
			{
				i + j * row_length,
				i + (j + 1) * row_length,
				(i + 1) + j * row_length,
				(i + 1) + (j + 1) * row_length,
			};

			// 添加三角形索引到网格对象，并根据 flip_winding 参数调整顶点顺序
			// 这里索引是对应到position的序号。
			m.raw_indices.emplace_back(index[0]);
			m.raw_indices.emplace_back(index[flip_winding ? 2 : 1]);
			m.raw_indices.emplace_back(index[flip_winding ? 1 : 2]);

			m.raw_indices.emplace_back(index[2]);
			m.raw_indices.emplace_back(index[flip_winding ? 3 : 1]);
			m.raw_indices.emplace_back(index[flip_winding ? 1 : 3]);
		}
	}

	// 索引数量检查
	const u32 num_indices{ 3 * 2 * horizontal_count * vertical_count };
	assert(m.raw_indices.size() == num_indices);

	m.uv_sets.resize(1);

	// 根据索引分配 UV 坐标
	// 对应的是每个顶点。
	for (u32 i{ 0 }; i < num_indices; ++i)
	{
		m.uv_sets[0].emplace_back(uvs[m.raw_indices[i]]);
	}

	return m;
}

mesh
create_uv_sphere(const primitive_init_info& info)
{
	const u32 phi_count{ clamp(info.segments[axis::x], 3u, 64u) };
	const u32 theta_count{ clamp(info.segments[axis::y], 2u, 64u) };
	const f32 theta_step{ pi / theta_count };
	const f32 phi_step{ two_pi / phi_count };
	const u32 num_vertices{ 2 + phi_count * (theta_count - 1) };
	const u32 num_indices{ 2 * 3 * phi_count + 2 * 3 * phi_count * (theta_count - 2) };


	mesh m{};
	m.name = "uv_sphere";
	m.positions.resize(num_vertices);

	// add the top vertex
	u32 c{ 0 };
	m.positions[c++] = { 0.f, info.size.y, 0.f };

	for (u32 j{ 1 }; j <= (theta_count - 1); ++j)
	{
		const f32 theta{ j * theta_step };
		for (u32 i{ 0 }; i < phi_count; ++i)
		{
			const f32 phi{ i * phi_step };
			m.positions[c++] = {
				info.size.x * XMScalarSin(theta) * XMScalarCos(phi),
				info.size.y * XMScalarCos(theta),
				-info.size.z * XMScalarSin(theta) * XMScalarSin(phi)
			};
		}
	}


	// add bottom vertex
	m.positions[c++] = { 0.f, -info.size.y,0.f };
	assert(c == num_vertices);

	c = 0;
	m.raw_indices.resize(num_indices);
	utl::vector<v2> uvs(num_indices);
	const f32 inv_theta_count{ 1.f / theta_count };
	const f32 inv_phi_count{ 1.f / phi_count };


	// indices for the top cap
	for (u32 i{ 0 }; i < phi_count - 1; ++i)
	{
		uvs[c] = { (2 * i + 1) * 0.5f * inv_phi_count, 1.f };
		m.raw_indices[c++] = 0;
		uvs[c] = { i * inv_phi_count, 1.f - inv_theta_count };
		m.raw_indices[c++] = i + 1;
		uvs[c] = { (i + 1) * inv_phi_count, 1.f - inv_theta_count };
		m.raw_indices[c++] = i + 2;
	}

	uvs[c] = { 1.f - 0.5f * inv_phi_count, 1.f };
	m.raw_indices[c++] = 0;
	uvs[c] = { 1.f - inv_phi_count, 1.f - inv_theta_count };
	m.raw_indices[c++] = phi_count;
	uvs[c] = { 1.f, 1.f - inv_theta_count };
	m.raw_indices[c++] = 1;

	// indices for the section
	for (u32 j{ 0 }; j < (theta_count - 2); ++j)
	{
		for (u32 i{ 0 }; i < (phi_count - 1); ++i)
		{
			const u32 index[4]{
				1 + i + j * phi_count,
				1 + i + (j + 1) * phi_count,
				1 + (i + 1) + (j + 1) * phi_count,
				1 + (i + 1) + j * phi_count,
			};
			uvs[c] = { (i)*inv_phi_count, 1.f - (j + 1) * inv_theta_count };
			m.raw_indices[c++] = index[0];
			uvs[c] = { (i)*inv_phi_count, 1.f - (j + 2) * inv_theta_count };
			m.raw_indices[c++] = index[1];
			uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 2) * inv_theta_count };
			m.raw_indices[c++] = index[2];

			uvs[c] = { (i)*inv_phi_count, 1.f - (j + 1) * inv_theta_count };
			m.raw_indices[c++] = index[0];
			uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 2) * inv_theta_count };
			m.raw_indices[c++] = index[2];
			uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 1) * inv_theta_count };
			m.raw_indices[c++] = index[3];
		}

		const u32 index[4]{
			phi_count + j * phi_count,
			phi_count + (j + 1) * phi_count,
			1 + (j + 1) * phi_count,
			1 + j * phi_count
		};

		uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 1) * inv_theta_count };
		m.raw_indices[c++] = index[0];
		uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 2) * inv_theta_count };
		m.raw_indices[c++] = index[1];
		uvs[c] = { (1.f), 1.f - (j + 2) * inv_theta_count };
		m.raw_indices[c++] = index[2];

		uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 1) * inv_theta_count };
		m.raw_indices[c++] = index[0];
		uvs[c] = { (1.f), 1.f - (j + 2) * inv_theta_count };
		m.raw_indices[c++] = index[2];
		uvs[c] = { (1.f), 1.f - (j + 1) * inv_theta_count };
		m.raw_indices[c++] = index[3];
	}

	// indices for the bottom
	const u32 south_pole_index{ (u32)m.positions.size() - 1 };
	for (u32 i{ 0 }; i < (phi_count - 1); ++i)
	{
		uvs[c] = { (2 * i + 1) * 0.5f * inv_phi_count, 0.f };
		m.raw_indices[c++] = south_pole_index;
		uvs[c] = { (i + 1) * inv_phi_count, inv_theta_count };
		m.raw_indices[c++] = south_pole_index - phi_count + i + 1;
		uvs[c] = { (i)*inv_phi_count, inv_theta_count };
		m.raw_indices[c++] = south_pole_index - phi_count + i;
	}

	uvs[c] = { 1.f - 0.5f * inv_phi_count, 0.f };
	m.raw_indices[c++] = south_pole_index;
	uvs[c] = { 1.f, inv_theta_count };
	m.raw_indices[c++] = south_pole_index - phi_count;
	uvs[c] = { 1.f - inv_phi_count, inv_theta_count };
	m.raw_indices[c++] = south_pole_index - 1;

	m.uv_sets.emplace_back(uvs);

	return m;
}

void
create_plane(scene& scene, const primitive_init_info& info)
{
	lod_group lod{};
	lod.name = "plane";
	lod.meshes.emplace_back(create_plane(info));
	scene.lod_groups.emplace_back(lod);
}
void
create_cube(scene& scene, const primitive_init_info& info)
{

}
void
create_uv_sphere(scene& scene, const primitive_init_info& info)
{
	lod_group lod{};
	lod.name = "uv_sphere";
	lod.meshes.emplace_back(create_uv_sphere(info));
	scene.lod_groups.emplace_back(lod);
}
void
create_ico_sphere(scene& scene, const primitive_init_info& info)
{

}
void
create_cylinder(scene& scene, const primitive_init_info& info)
{
}
void
create_capsule(scene& scene, const primitive_init_info& info)
{
}

static_assert(_countof(creators) == primitive_mesh_type::count);

} // namespace


EDITOR_INTERFACE void
CreatePrimitiveMesh(scene_data* data, primitive_init_info* info)
{
	assert(data && info);
	assert(info->type < primitive_mesh_type::count);
	scene scene{};

	creators[info->type](scene, *info);

	data->settings.calculate_normals = 1;
	process_scene(scene, data->settings);
	pack_data(scene, *data);
}

}