#pragma once
/**
 * @file geometry.h
 * @brief 定义UI组件的几何处理类
 *
 * 本文件定义了geometry_t类，用于管理UI组件的几何属性、坐标变换和空间关系。
 * 它处理组件在不同坐标系（本地、父级、绝对/屏幕）之间的转换。
 */

#include <Eigen/Core>

#include "layout_transform.h"
#include "rect.h"

/**
 * @class geometry_t
 * @brief 几何处理类，管理UI组件的尺寸和坐标变换
 *
 * geometry_t维护组件的尺寸和变换矩阵，处理不同坐标系之间的转换，
 * 并提供创建子几何体、应用变换等功能。几何体本质上代表了UI组件
 * 在界面中的位置、大小和变换状态。
 */
class geometry_t {
public:
    //-------------- 构造函数 --------------

    /**
     * @brief 默认构造函数
     *
     * 创建一个空的几何体，大小为零且无变换。
     */
    geometry_t() : size_(), local_to_parent_(), local_to_window_() {}

    /**
     * @brief 构造具有特定大小和变换的几何体
     *
     * @param in_size 组件的本地大小
     * @param in_local_to_parent 本地到父级的变换
     * @param in_local_to_window 本地到窗口坐标系的变换
     */
    geometry_t(Eigen::Vector2f in_size, transform2d in_local_to_parent, transform2d in_local_to_window)
        : size_(std::move(in_size)), local_to_parent_(std::move(in_local_to_parent)), local_to_window_(std::move(in_local_to_window)) {
    }

    //-------------- 子几何体创建 --------------

    /**
     * @brief 创建一个相对于此几何体定位的子几何体
     *
     * @param in_local_offset 相对于父级的位置偏移
     * @param in_child_size 子级的大小
     * @param in_scale 子级的缩放
     * @return 新的子几何体
     */
    [[nodiscard]] auto make_child(const Eigen::Vector2f& in_local_offset, const Eigen::Vector2f& in_child_size, float in_scale = 1.f) const {
        // 创建从子级到此几何体的变换
        transform2d child_to_parent;
        child_to_parent.set_transform(in_local_offset, 0.0f, Eigen::Vector2f(in_scale, in_scale));
        child_to_parent = local_to_parent_.concatenate(child_to_parent);

    	transform2d child_to_window;
    	child_to_window.set_transform(in_local_offset, 0.0f, Eigen::Vector2f(in_scale, in_scale));
    	child_to_window = local_to_window_.concatenate(child_to_window);

        return geometry_t(in_child_size, child_to_parent, child_to_window);
    }

    //-------------- 变换和变换矩阵获取 --------------

    /**
     * @brief 获取本地到父级的变换
     * @return 本地到父级的变换矩阵
     */
    [[nodiscard]] const auto& get_local_to_parent_transform() const
    {
        return local_to_parent_;
    }

    /**
     * @brief 获取本地到绝对的变换
     * @return 本地到绝对坐标的变换矩阵
     */
    [[nodiscard]] const auto& get_local_to_window_transform() const
    {
        return local_to_window_;
    }

    //-------------- 坐标转换 --------------

    /**
     * @brief 将点从本地坐标转换到绝对坐标
     * @param in_local_point 本地坐标中的点
     * @return 绝对坐标中的点
     */
    [[nodiscard]] auto local_to_window(const Eigen::Vector2f& in_local_point) const
    {
        return local_to_window_.transform_point(in_local_point);
    }

    /**
     * @brief 将点从绝对坐标转换到本地坐标
     * @param in_window_point 绝对坐标中的点
     * @return 本地坐标中的点
     */
    [[nodiscard]] auto window_to_local(const Eigen::Vector2f& in_window_point) const
    {
        return local_to_window_.inverse_transform_point(in_window_point);
    }

    /**
     * @brief 将点从本地坐标转换到父级坐标
     * @param in_local_point 本地坐标中的点
     * @return 父级坐标中的点
     */
    [[nodiscard]] auto local_to_parent(const Eigen::Vector2f& in_local_point) const
    {
        return local_to_parent_.transform_point(in_local_point);
    }

    /**
     * @brief 将点从父级坐标转换到本地坐标
     * @param in_parent_point 父级坐标中的点
     * @return 本地坐标中的点
     */
    [[nodiscard]] auto parent_to_local(const Eigen::Vector2f& in_parent_point) const
    {
        return local_to_parent_.inverse_transform_point(in_parent_point);
    }

    //-------------- 大小和位置获取 --------------

    /**
     * @brief 获取此几何体的本地大小
     * @return 本地坐标系中的大小向量
     */
    [[nodiscard]] const auto& get_local_size() const { return size_; }

    /**
     * @brief 设置此几何体的本地大小
     * @param in_size 新的本地大小
     */
    void set_local_size(const Eigen::Vector2f& in_size) { size_ = in_size; }

    /**
     * @brief 获取本地坐标系中的矩形
     * @return 表示几何体大小的矩形，左上角位于原点
     */
    [[nodiscard]] auto get_local_rect() const { return rect_t({0, 0}, size_); }

    /**
     * @brief 获取此几何体在窗口坐标中的位置
     * @return 绝对坐标中的位置（左上角）
     */
    [[nodiscard]] auto get_window_position() const
    {
        return local_to_window(Eigen::Vector2f(0, 0));
    }

    /**
     * @brief 获取几何体的缩放因子
     * @return 当前几何体的缩放向量
     */
    [[nodiscard]] auto get_scale() const
    {
        // 从本地到绝对变换中提取缩放分量
        return local_to_window_.get_scale();
    }

    /**
     * @brief 获取绝对坐标系中的矩形
     * @return 表示几何体在绝对坐标系中的矩形
     */
    [[nodiscard]] auto get_window_rect() const
    {
        // 变换本地矩形的四个角到绝对坐标
        const auto& top_left = local_to_window(Eigen::Vector2f(0, 0));
        const auto& bottom_right = local_to_window(size_);

        const Eigen::Vector2f& absolute_size = bottom_right - top_left;

        return rect_t(top_left, absolute_size);
    }

    //-------------- 几何变换 --------------

    /**
     * @brief 创建一个基于当前几何体但应用偏移的新几何体
     *
     * @param in_offset 要应用的偏移
     * @return 新的几何体
     */
    [[nodiscard]] auto with_new_offset(const Eigen::Vector2f& in_offset) const
    {
        // 创建一个表示偏移的变换（从缩小后的空间到原始空间）
        transform2d offset_transform;
        offset_transform.set_transform(in_offset, 0.0f, Eigen::Vector2f(1.0f, 1.0f));

        // 更新变换 - 先应用原变换，再应用偏移变换
        auto local_to_parent = local_to_parent_.concatenate(offset_transform);
        auto local_to_absolute = local_to_window_.concatenate(offset_transform);
        return geometry_t(size_, local_to_parent, local_to_absolute);
    }

    /**
     * @brief 创建一个缩放版本的几何体
     *
     * @param in_scale_amount 要应用的缩放因子
     * @return 新的几何体
     */
    [[nodiscard]] auto with_scale(float in_scale_amount) const
    {
        // 获取当前变换的组件
        const auto& current_scale = local_to_parent_.get_scale();
        const auto& current_trans = local_to_parent_.get_translation();

        // 创建新的本地到父级变换
        transform2d new_local_to_parent;
        new_local_to_parent.set_transform(
            current_trans,
            0.0f,
            Eigen::Vector2f(current_scale.x() * in_scale_amount, current_scale.y() * in_scale_amount)
        );

        // 计算新的本地到绝对变换
        const transform2d parent_to_window = local_to_window_.concatenate(local_to_parent_.inverse());

        return geometry_t(size_, new_local_to_parent, parent_to_window);
    }

    /**
     * @brief 按照边距偏移变换
     *
     * @param in_margin 要缩小的边距
     *
     * 根据给定的边距缩小几何体，并应用相应的变换使得内容位置正确。
     */
    void shrink(const margin_t& in_margin) {
        size_ -= in_margin.size();
        // 获取左上角偏移
        const Eigen::Vector2f offset = in_margin.top_left();

        // 创建一个表示偏移的变换（从缩小后的空间到原始空间）
        transform2d offset_transform;
        offset_transform.set_transform(offset, 0.0f, Eigen::Vector2f(1.0f, 1.0f));

        // 更新变换 - 先应用原变换，再应用偏移变换
        local_to_parent_ = local_to_parent_.concatenate(offset_transform);
        local_to_window_ = local_to_window_.concatenate(offset_transform);
    }

    //-------------- 测试和查询 --------------

    /**
     * @brief 检查点是否在此几何体内
     *
     * @param in_local_point 以局部坐标表示的点
     * @return 如果点在几何体内，则为true
     */
	[[nodiscard]] auto is_under_local_location(const Eigen::Vector2f& in_local_point) const {
        // 检查点是否在本地矩形范围内
        return in_local_point.x() >= 0 &&
        	in_local_point.x() <= size_.x() &&
        	in_local_point.y() >= 0 &&
        	in_local_point.y() <= size_.y();
    }

    /**
     * @brief 检查点是否在此几何体内
     *
     * @param in_window_point 以绝对坐标表示的点, 窗口内的点
     * @return 如果点在几何体内，返回局部坐标系中的点
     */
    [[nodiscard]] auto is_under_location(const Eigen::Vector2f& in_window_point) const
    {
        // 将点从绝对坐标转换到本地坐标
    	const auto& local_point = window_to_local(in_window_point);
		if (is_under_local_location(local_point))
			return std::optional(local_point);
    	return std::optional<Eigen::Vector2f>();
    }
private:
    /** 几何体在本地坐标系中的大小 */
    Eigen::Vector2f size_;

    /** 从本地坐标系到父级坐标系的变换 */
    transform2d local_to_parent_;

    /** 从本地坐标系到绝对坐标系的变换 */
    transform2d local_to_window_;
};
