/**
 * @file GameEntity.h
 * @brief 实体句柄、脚本基类与脚本注册机制定义。
 * @details
 * 该文件是引擎 ECS 对外脚本接口的核心入口：
 * - game_entity::entity 提供实体句柄及常用变换访问；
 * - script::entity_script 定义脚本生命周期与变换写接口；
 * - script::detail 提供脚本工厂注册、按名称哈希检索与宏封装。
 */
#pragma once
#include "..\Components\ComponentsCommon.h"
#include "TransformComponent.h"
#include "ScriptComponent.h"

namespace XEngine{
namespace game_entity {

/**
 * @brief 实体强类型标识符。
 */
DEFINE_TYPED_ID(entity_id);

/**
 * @brief 运行时实体句柄封装。
 */
class entity {
public:
	/**
	 * @brief 由实体 ID 构造句柄。
	 */
	constexpr explicit entity(entity_id id) : _id{ id } {}
	/**
	 * @brief 构造无效实体句柄。
	 */
	constexpr entity() : _id{ id::invalid_id } {}
	/**
	 * @brief 获取实体 ID。
	 */
	[[nodiscard]] constexpr entity_id get_id() const { return _id; }
	/**
	 * @brief 判断实体句柄是否有效。
	 */
	[[nodiscard]] constexpr bool is_valid() const { return id::is_valid(_id); }

	/**
	 * @brief 获取实体变换组件句柄。
	 */
	[[nodiscard]] transform::component transform() const;
	/**
	 * @brief 获取实体脚本组件句柄。
	 */
	[[nodiscard]] script::component script() const;

	[[nodiscard]] math::v4 rotation() const { return transform().rotation(); }
	[[nodiscard]] math::v3 orientation() const { return transform().orientation(); }
	[[nodiscard]] math::v3 position() const { return transform().position(); }
	[[nodiscard]] math::v3 scale() const { return transform().scale(); }
private:
	entity_id _id;
};
}


namespace script {
/**
 * @brief 实体脚本基类。
 * @details
 * 派生类可覆盖 begin_play/update，并通过受保护接口修改实体变换。
 */
class entity_script : public game_entity::entity {
public:
	/**
	 * @brief 析构脚本实例。
	 */
	virtual ~entity_script() = default;
	/**
	 * @brief 脚本启动回调。
	 */
	virtual void begin_play() {}
	/**
	 * @brief 脚本逐帧更新回调。
	 * @param dt 帧时间步长。
	 */
	virtual void update(float) {}

protected:
	constexpr explicit entity_script(game_entity::entity entity)
		: game_entity::entity{ entity.get_id() }{}

	void set_rotation(math::v4 rotation_quaternion) const {set_rotation(this, rotation_quaternion);}
	void set_orientation(math::v3 orientation_vector) const {set_orientation(this, orientation_vector);}
	void set_position(math::v3 position) const {set_position(this, position);}
	void set_scale(math::v3 scale) const { set_scale(this, scale); }

	static void set_rotation(const game_entity::entity *const entity, math::v4 rotation_quaternion);
	static void set_orientation(const game_entity::entity *const entity, math::v3 orientation_vector);
	static void set_position(const game_entity::entity *const entity, math::v3 position);
	static void set_scale(const game_entity::entity *const entity, math::v3 scale);

};


namespace detail {

/**
 * @brief 脚本实例智能指针类型。
 */
using script_ptr = std::unique_ptr<entity_script>;
#ifdef USESTDFUNC
/**
 * @brief 基于 std::function 的脚本工厂签名。
 */
using script_creator = std::function<script_ptr(game_entity::entity entity)>;
#else
/**
 * @brief 基于函数指针的脚本工厂签名。
 */
using script_creator = script_ptr(*)(game_entity::entity entity);
#endif //  USESTDFUNC
/**
 * @brief 脚本名哈希器类型。
 */
using string_hash = std::hash<std::string>;

/**
 * @brief 注册脚本工厂到脚本注册表。
 * @param tag 脚本哈希标签。
 * @param creator 脚本工厂函数。
 * @return 注册结果码。
 */
u8 register_script(size_t tag, script_creator creator);
#ifdef USE_WITH_EDITOR 
extern "C" __declspec(dllexport)
#endif
/**
 * @brief 按哈希标签查询脚本工厂。
 * @param tag 脚本名哈希。
 * @return 对应脚本工厂函数。
 */
script_creator get_script_creator(size_t tag);

/**
 * @brief 默认脚本工厂模板函数。
 * @tparam script_class 脚本类型。
 * @param entity 绑定实体。
 * @return 新创建脚本实例。
 */
template<class script_class>
script_ptr create_script(game_entity::entity entity)
{
	assert(entity.is_valid());
	return std::make_unique<script_class>(entity);
}


#ifdef USE_WITH_EDITOR
/**
 * @brief 向编辑器导出脚本名称。
 * @param name 脚本类名。
 * @return 写入结果码。
 */
u8 add_script_name(const char* name);

#define REGISTER_SCRIPT(TYPE)																			\
		namespace {																						\
		u8 _reg_##TYPE{																					\
		XEngine::script::detail::register_script(														\
		XEngine::script::detail::string_hash()(#TYPE),													\
		&XEngine::script::detail::create_script<TYPE>) };												\
		const u8 _name_##TYPE																			\
		{ XEngine::script::detail::add_script_name(#TYPE) };											\
		}																								
		

#else
#define REGISTER_SCRIPT(TYPE)																			\
		class TYPE;																						\
		namespace {																						\
		u8 _reg_##TYPE{																					\
		XEngine::script::detail::register_script(														\
		XEngine::script::detail::string_hash()(#TYPE),													\
		&XEngine::script::detail::create_script<TYPE>) };												\
		}

#endif // USE_WITH_EDITOR


}//namespace detail
}//namespace script

}
