#pragma once
/**
 * @file transform2d.h
 * @brief 定义2D变换矩阵类
 *
 * 本文件定义了transform2d类，用于处理2D空间中的变换操作，
 * 包括平移、旋转和缩放。该类使用3x3矩阵表示齐次坐标变换。
 */

#include <Eigen/Eigen>

/**
 * @class transform2d
 * @brief 2D变换矩阵类，处理2D空间的平移、旋转和缩放
 *
 * transform2d封装了2D变换所需的数学操作，使用3x3的齐次坐标矩阵
 * 实现点和向量的变换。它提供了设置和应用变换的各种方法，以及
 * 组合和逆变换等操作。
 */
class transform2d {
public:
    //-------------- 构造和基本设置 --------------

    /**
     * @brief 默认构造函数
     *
     * 创建一个单位变换，不改变任何被变换的点或向量。
     */
    transform2d() : matrix_(Eigen::Matrix3f::Identity()),
                    translation_({ 0, 0 }),
                    scale_({ 1, 1 }),
                    rotation_(0) {
    }

    /**
     * @brief 带参数的构造函数
     * @param in_translation 平移向量
     * @param in_rotation 旋转角度（弧度）
     * @param in_scale 缩放向量
     *
     * 使用指定的平移、旋转和缩放参数创建变换。
     */
    transform2d(const Eigen::Vector2f& in_translation, float in_rotation, const Eigen::Vector2f& in_scale);

    /**
     * @brief 设置变换参数
     * @param in_translation 平移向量
     * @param in_rotation 旋转角度（弧度），默认为0
     * @param in_scale 缩放向量，默认为单位缩放
     *
     * 一次性设置变换的平移、旋转和缩放参数。
     */
    void set_transform(const Eigen::Vector2f& in_translation, float in_rotation = 0.f, const Eigen::Vector2f& in_scale = {});

    //-------------- 获取属性 --------------

    /**
     * @brief 获取当前缩放值
     * @return 缩放向量
     */
    [[nodiscard]] const auto& get_scale() const { return scale_; }

    /**
     * @brief 获取当前平移值
     * @return 平移向量
     */
    [[nodiscard]] const auto& get_translation() const { return translation_; }

    /**
     * @brief 获取变换矩阵
     * @return 3x3变换矩阵的常引用
     */
    [[nodiscard]] const auto& get_matrix() const { return matrix_; }

    /**
     * @brief 检查是否为单位变换
     * @return 如果是单位变换则返回true
     *
     * 判断当前变换是否为单位变换（不改变被变换的点或向量）。
     */
    [[nodiscard]] auto is_identity() const {
        return matrix_.isIdentity();
    }

    //-------------- 变换操作 --------------

    /**
     * @brief 添加平移
     * @param in_delta 要添加的平移向量
     *
     * 在当前变换上添加平移。
     */
    void translate(const Eigen::Vector2f& in_delta);

    /**
     * @brief 添加旋转
     * @param in_angle 要添加的旋转角度（弧度）
     *
     * 在当前变换上添加旋转。
     */
    void rotate(float in_angle);

    /**
     * @brief 添加缩放
     * @param in_scale 要添加的缩放向量
     *
     * 在当前变换上添加缩放。
     */
    void scale(const Eigen::Vector2f& in_scale);

    //-------------- 点和向量变换 --------------

    /**
     * @brief 变换点坐标
     * @tparam T 坐标类型（通常是float或double）
     * @param in_point 要变换的点
     * @return 变换后的点
     *
     * 将当前变换应用到点上。
     */
    template<typename T>
    auto transform_point(const Eigen::Vector2<T>& in_point) const {
        Eigen::Vector3<T> homogeneous_point(in_point.x(), in_point.y(), 1);
        auto transformed = matrix_.cast<T>() * homogeneous_point;
        return Eigen::Vector2<T>(
            transformed.x() / transformed.z(),
            transformed.y() / transformed.z()
        );
    }

    /**
     * @brief 逆变换点坐标
     * @tparam T 坐标类型（通常是float或double）
     * @param in_point 要逆变换的点
     * @return 逆变换后的点
     *
     * 将当前变换的逆变换应用到点上。
     */
    template<typename T>
    auto inverse_transform_point(const Eigen::Vector2<T>& in_point) const {
        const auto& inverse = matrix_.inverse();
        Eigen::Vector3<T> homogeneous_point(in_point.x(), in_point.y(), 1);
        auto transformed = inverse.cast<T>() * homogeneous_point;
        return Eigen::Vector2<T>(
            transformed.x() / transformed.z(),
            transformed.y() / transformed.z()
        );
    }

    /**
     * @brief 变换向量
     * @tparam T 坐标类型（通常是float或double）
     * @param in_vector 要变换的向量
     * @return 变换后的向量
     *
     * 将当前变换应用到向量上。
     * 注意：向量变换不考虑平移分量。
     */
    template<typename T>
    auto transform_vector(const Eigen::Vector2<T>& in_vector) const {
        Eigen::Vector3<T> homogeneous_vector(in_vector.x(), in_vector.y(), 0);
        auto transformed = matrix_.cast<T>() * homogeneous_vector;
        return Eigen::Vector2<T>(transformed.x(), transformed.y());
    }

    /**
     * @brief 逆变换向量
     * @tparam T 坐标类型（通常是float或double）
     * @param in_vector 要逆变换的向量
     * @return 逆变换后的向量
     *
     * 将当前变换的逆变换应用到向量上。
     * 注意：向量变换不考虑平移分量。
     */
    template<typename T>
    auto inverse_transform_vector(const Eigen::Vector2<T>& in_vector) const {
        const auto& inverse = matrix_.inverse();
        Eigen::Vector3<T> homogeneous_vector(in_vector.x(), in_vector.y(), 0);
        auto transformed = inverse.cast<T>() * homogeneous_vector;
        return Eigen::Vector2<T>(transformed.x(), transformed.y());
    }

    //-------------- 复合变换和特殊操作 --------------

    /**
     * @brief 组合两个变换
     * @param in_other 要组合的另一个变换
     * @return 组合后的新变换
     *
     * 将当前变换与另一个变换组合，等效于矩阵乘法。
     */
    [[nodiscard]] auto combine(const transform2d& in_other) const {
        transform2d result;
        result.matrix_ = matrix_ * in_other.matrix_;
        return result;
    }

    /**
     * @brief 计算逆变换
     * @return 当前变换的逆变换
     *
     * 创建并返回当前变换的逆变换。
     */
    [[nodiscard]] auto inverse() const {
        transform2d result;
        // 逆变换的缩放是原缩放的倒数
        result.scale_ = Eigen::Vector2f(1.0f / scale_.x(), 1.0f / scale_.y());

        // 逆变换的平移需要考虑缩放
        result.translation_ = Eigen::Vector2f(
            -translation_.x() / scale_.x(),
            -translation_.y() / scale_.y()
        );

        // 逆变换的旋转是原旋转的负值
        result.rotation_ = -rotation_;

        result.update_matrix();
        return result;
    }

    /**
     * @brief 连接两个布局变换
     * @param rhs 右侧变换
     * @return 连接后的新变换
     *
     * 连接两个布局变换，实现与UnrealEngine的FSlateLayoutTransform::Concatenate相同的功能。
     * 这个函数通过先应用LHS变换然后应用RHS变换来工作。
     * 用矩阵形式表示如下:
     * [ Sa  0   0 ]   [ Sb  0   0 ]
     * [ 0   Sa  0 ] * [ 0   Sb  0 ]
     * [ Tax Tay 1 ]   [ Tbx Tby 1 ]
     */
    [[nodiscard]] auto concatenate(const transform2d& rhs) const
    {
        // 新的缩放是两个缩放的元素级乘积
        const Eigen::Vector2f new_scale(scale_.x() * rhs.scale_.x(),
                                      scale_.y() * rhs.scale_.y());

        // 新的平移是当前平移通过RHS变换后的结果
        // 等价于 RHS.TransformPoint(Translation)
        const Eigen::Vector2f new_translation(translation_.x() * rhs.scale_.x() + rhs.translation_.x(),
                                           translation_.y() * rhs.scale_.y() + rhs.translation_.y());

        // 创建并返回具有这些值的新变换
        transform2d result;
        result.scale_      = new_scale;
        result.translation_ = new_translation;

        // 布局变换中不考虑旋转
        result.rotation_ = 0.0f;

        // 更新内部矩阵
        result.update_matrix();

        return result;
    }

private:
    /**
     * @brief 更新内部矩阵
     *
     * 根据当前的平移、旋转和缩放值更新内部的变换矩阵。
     */
    void update_matrix();

    /** 3x3变换矩阵 */
    Eigen::Matrix3f matrix_;

    /** 平移向量 */
    Eigen::Vector2f translation_;

    /** 缩放向量 */
    Eigen::Vector2f scale_;

    /** 旋转角度（弧度） */
    float           rotation_;
};
