#pragma once
/**
 * @file mcompound_widget.h
 * @brief 定义只能包含单个子组件的复合组件基类
 *
 * 本文件定义了mcompound_widget模板类，这是一种只能包含一个子组件的复合组件。
 * 通过模板参数SlotType可以自定义子组件插槽的具体行为。
 */

#include "mwidget.h"
#include "widget_utils/layout_utils.h"
#include "slot_util.h"

/**
 * @struct mcompound_widget_slot
 * @brief 复合组件的插槽结构，管理子组件的布局属性
 *
 * @tparam T 插槽的具体类型，通常是派生自此模板的类
 *
 * 提供了对子组件进行水平和垂直对齐的属性，以及内容管理功能。
 * 使用SLOT宏来定义插槽的标准功能。
 */
template<typename T>
struct mcompound_widget_slot {
    /**
     * @brief 获取当前实例的引用，转换为具体类型T
     * @return 当前实例的引用，类型为T&
     */
    auto& me() {
        return static_cast<T&>(*this);
    }
	std::weak_ptr<mwidget> slot_owner;

	operator std::shared_ptr<mwidget>() const { return slot_owner.lock(); }

    // 插槽功能宏 - 定义内容管理和对齐属性
    SLOT_CONTENT()
    SLOT_ATTRIBUTE(horizontal_alignment_t, h_alignment)
    SLOT_ATTRIBUTE(vertical_alignment_t, v_alignment)
};

/**
 * @class mcompound_widget
 * @brief 仅允许一个子组件的复合组件基类
 *
 * @tparam SlotType 插槽类型，控制如何管理子组件
 *
 * 这个类是所有只需要单个子组件的组件的基类。它处理子组件的
 * 尺寸计算、布局和绘制等常见任务。
 */
template<typename SlotType>
class mcompound_widget : public mwidget {
public:
	using slot_type = SlotType;
    //-------------- 内容设置 --------------

    /**
     * @brief 设置子组件内容
     * @param in_widget 要设置为内容的组件
     * @return 子组件插槽的引用，允许链式调用设置插槽属性
     *
     * 设置组件的子内容，并正确配置窗口和父子关系。
     */
    auto& set_content(const std::shared_ptr<mwidget>& in_widget) {
        on_set_content(in_widget);
    	invalidate(invalidate_reason::layout);
        auto shared_this = shared_from_this();

    	auto& slot      = get_child_slot();
    	slot.slot_owner = shared_this;
    	slot.set(in_widget);
    	in_widget->set_parent(shared_this);
    	return slot;
    }

    //-------------- 尺寸计算 --------------

    /**
     * @brief 计算组件的期望大小
     * @param in_layout_scale_multiplier 布局缩放系数
     * @return 组件的期望大小
     *
     * 如果有子组件且不是collapsed状态，则返回子组件的期望大小。
     */
    [[nodiscard]] virtual Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override;

    //-------------- 绘制和布局 --------------

    /**
     * @brief 绘制组件
     * @param in_context 绘制上下文
     *
     * 基类实现为空，因为复合组件通常不需要自己绘制，而是依赖子组件的绘制。
     * 派生类可以根据需要重写此函数。
     */
    virtual void on_paint(mirage_paint_context& in_context) override {}

    /**
     * @brief 排列子组件
     * @param in_allotted_geometry 分配给组件的几何区域
     *
     * 根据插槽中的对齐属性对单个子组件进行排列。
     */
    virtual void arrange_children(const geometry_t& in_allotted_geometry) override;

	SlotType& get_child_slot() { return slot_; }
	const SlotType& get_child_slot() const { return slot_; }

	auto& operator[](const std::shared_ptr<mwidget>& in_widget) {
		return set_content(in_widget);
	}

protected:
    /**
     * @brief 在设置内容时的回调函数
     * @param in_widget 被设置为内容的组件
     *
     * 派生类可以重写此函数来处理子组件设置时的特殊逻辑。
     * 基类实现为空。
     */
    virtual void on_set_content(const std::shared_ptr<mwidget>& in_widget) {}

	SlotType slot_;
};

/**
 * @brief 计算复合组件的期望大小
 *
 * 如果有子组件且不是collapsed状态，则返回子组件的期望大小，否则返回零大小。
 */
template<typename SlotType>
Eigen::Vector2f mcompound_widget<SlotType>::compute_desired_size(float in_layout_scale_multiplier) const {
    if (const auto& child_widget = get_child_slot().get()) {
	    if (has_any_flag(child_widget->get_visibility(), visibility_t::any_visible)) {
            return child_widget->compute_desired_size(in_layout_scale_multiplier);
        }
    }
    return {};
}

/**
 * @brief 排列复合组件的子组件
 *
 * 使用layout_utils中的arrange_single_child函数根据对齐属性排列单个子组件。
 */
template<typename SlotType>
void mcompound_widget<SlotType>::arrange_children(const geometry_t& in_allotted_geometry) {
	const auto& slot = get_child_slot();
    if (const auto& child_widget = slot.get()) {
	    arrange_single_child(in_allotted_geometry, child_widget, slot.h_alignment(), slot.v_alignment(), {});
    }
}
