#include "pch.h"
#include "BoundingBox.h"
#include <limits>
#include <stdexcept>
#include <algorithm>
#include <cmath>
#include <sstream>
#include <iomanip>


namespace Vivid2D::util {

    // ==================== 构造函数 ====================

    BoundingBox::BoundingBox() {
        reset();
    }

    BoundingBox::BoundingBox(float minX, float minY, float maxX, float maxY) {
        set(minX, minY, maxX, maxY);
    }

    BoundingBox::BoundingBox(const glm::vec2& point1, const glm::vec2& point2) {
        set(point1, point2);
    }

    BoundingBox::BoundingBox(const BoundingBox& other) {
        set(other);
    }

    BoundingBox::BoundingBox(const std::vector<glm::vec2>& points) {
        set(points);
    }

    BoundingBox::BoundingBox(const std::vector<float>& vertices) {
        set(vertices);
    }


    // ==================== 设置方法 ====================

    void BoundingBox::reset() {
        m_Min = glm::vec2(std::numeric_limits<float>::max());
        m_Max = glm::vec2(-std::numeric_limits<float>::max()); // Or std::numeric_limits<float>::lowest()
        m_IsValid = false;
    }

    void BoundingBox::set(float minX, float minY, float maxX, float maxY) {
        if (minX > maxX || minY > maxY) {
            throw std::invalid_argument("Min values must be less than or equal to max values");
        }
        m_Min = glm::vec2(minX, minY);
        m_Max = glm::vec2(maxX, maxY);
        m_IsValid = true;
    }

    void BoundingBox::set(const glm::vec2& point1, const glm::vec2& point2) {
        reset();
        expand(point1);
        expand(point2);
    }

    void BoundingBox::set(const BoundingBox& other) {
        m_Min = other.m_Min;
        m_Max = other.m_Max;
        m_IsValid = other.m_IsValid;
    }

    void BoundingBox::set(const std::vector<glm::vec2>& points) {
        reset();
        for (const auto& point : points) {
            expand(point);
        }
    }

    void BoundingBox::set(const std::vector<float>& vertices) {
        reset();
        if (vertices.size() % 2 != 0) {
            throw std::invalid_argument("Vertices array must have an even number of elements");
        }
        for (size_t i = 0; i < vertices.size(); i += 2) {
            expand(vertices[i], vertices[i + 1]);
        }
    }

    // ==================== 扩展方法 ====================

    void BoundingBox::expand(float x, float y) {
        if (!m_IsValid) {
            m_Min = glm::vec2(x, y);
            m_Max = glm::vec2(x, y);
            m_IsValid = true;
        }
        else {
            m_Min.x = std::min(m_Min.x, x);
            m_Min.y = std::min(m_Min.y, y);
            m_Max.x = std::max(m_Max.x, x);
            m_Max.y = std::max(m_Max.y, y);
        }
    }

    void BoundingBox::expand(const glm::vec2& point) {
        expand(point.x, point.y);
    }

    void BoundingBox::expand(const BoundingBox& other) {
        if (!other.m_IsValid) return;
        if (!m_IsValid) {
            set(other);
        }
        else {
            expand(other.m_Min);
            expand(other.m_Max);
        }
    }

    void BoundingBox::expand(const std::vector<glm::vec2>& points) {
        for (const auto& point : points) {
            expand(point);
        }
    }

    void BoundingBox::expand(const std::vector<float>& vertices) {
        if (vertices.size() % 2 != 0) {
            throw std::invalid_argument("Vertices array must have an even number of elements");
        }
        for (size_t i = 0; i < vertices.size(); i += 2) {
            expand(vertices[i], vertices[i + 1]);
        }
    }

    // ==================== 转换方法 ====================

    BoundingBox BoundingBox::transform(const glm::mat3& matrix) const {
        if (!m_IsValid) return BoundingBox();

        auto corners = getCorners();
        BoundingBox result;

        for (const auto& corner : corners) {
            // Transform a 2D point using a 3x3 matrix by using homogeneous coordinates (x, y, 1)
            glm::vec3 transformed = matrix * glm::vec3(corner, 1.0f);
            result.expand(glm::vec2(transformed));
        }
        return result;
    }

    BoundingBox BoundingBox::translate(float dx, float dy) const {
        if (!m_IsValid) return BoundingBox();
        return BoundingBox(m_Min.x + dx, m_Min.y + dy, m_Max.x + dx, m_Max.y + dy);
    }

    BoundingBox BoundingBox::translate(const glm::vec2& translation) const {
        return translate(translation.x, translation.y);
    }

    BoundingBox BoundingBox::scale(float sx, float sy) const {
        if (!m_IsValid) return BoundingBox();
        return BoundingBox(m_Min.x * sx, m_Min.y * sy, m_Max.x * sx, m_Max.y * sy);
    }

    BoundingBox BoundingBox::scale(float scale) const {
        return this->scale(scale, scale);
    }

    BoundingBox BoundingBox::scale(const glm::vec2& scale) const {
        return this->scale(scale.x, scale.y);
    }


    // ==================== 几何计算 ====================

    std::vector<glm::vec2> BoundingBox::getCorners() const {
        if (!m_IsValid) return {};
        return {
            {m_Min.x, m_Min.y}, // Bottom-left
            {m_Max.x, m_Min.y}, // Bottom-right
            {m_Max.x, m_Max.y}, // Top-right
            {m_Min.x, m_Max.y}  // Top-left
        };
    }

    glm::vec2 BoundingBox::getCenter() const {
        return m_IsValid ? (m_Min + m_Max) * 0.5f : glm::vec2(0.0f);
    }

    glm::vec2 BoundingBox::getSize() const {
        return m_IsValid ? m_Max - m_Min : glm::vec2(0.0f);
    }

    glm::vec2 BoundingBox::getHalfSize() const {
        return m_IsValid ? (m_Max - m_Min) * 0.5f : glm::vec2(0.0f);
    }

    float BoundingBox::getArea() const {
        if (!m_IsValid) return 0.0f;
        glm::vec2 size = getSize();
        return size.x * size.y;
    }

    float BoundingBox::getPerimeter() const {
        if (!m_IsValid) return 0.0f;
        glm::vec2 size = getSize();
        return 2.0f * (size.x + size.y);
    }

    // ==================== 交叉测试 ====================

    bool BoundingBox::contains(float x, float y) const {
        if (!m_IsValid) return false;
        return x >= m_Min.x && x <= m_Max.x && y >= m_Min.y && y <= m_Max.y;
    }

    bool BoundingBox::contains(const glm::vec2& point) const {
        return contains(point.x, point.y);
    }

    bool BoundingBox::contains(const BoundingBox& other) const {
        if (!m_IsValid || !other.m_IsValid) return false;
        return other.m_Min.x >= m_Min.x && other.m_Max.x <= m_Max.x &&
            other.m_Min.y >= m_Min.y && other.m_Max.y <= m_Max.y;
    }

    bool BoundingBox::intersects(const BoundingBox& other) const {
        if (!m_IsValid || !other.m_IsValid) return false;
        return !(other.m_Max.x < m_Min.x || other.m_Min.x > m_Max.x ||
            other.m_Max.y < m_Min.y || other.m_Min.y > m_Max.y);
    }

    BoundingBox BoundingBox::intersection(const BoundingBox& other) const {
        if (!intersects(other)) return BoundingBox(); // Return invalid
        return BoundingBox(
            std::max(m_Min.x, other.m_Min.x),
            std::max(m_Min.y, other.m_Min.y),
            std::min(m_Max.x, other.m_Max.x),
            std::min(m_Max.y, other.m_Max.y)
        );
    }

    BoundingBox BoundingBox::getUnion(const BoundingBox& other) const {
        BoundingBox result(*this);
        result.expand(other);
        return result;
    }


    // ==================== 实用方法 ====================

    BoundingBox BoundingBox::inflate(float amount) const {
        return inflate(amount, amount);
    }

    BoundingBox BoundingBox::inflate(float dx, float dy) const {
        if (!m_IsValid) return BoundingBox();
        return BoundingBox(m_Min.x - dx, m_Min.y - dy, m_Max.x + dx, m_Max.y + dy);
    }

    BoundingBox BoundingBox::deflate(float amount) const {
        return deflate(amount, amount);
    }

    BoundingBox BoundingBox::deflate(float dx, float dy) const {
        if (!m_IsValid) return BoundingBox();
        float newMinX = m_Min.x + dx;
        float newMinY = m_Min.y + dy;
        float newMaxX = m_Max.x - dx;
        float newMaxY = m_Max.y - dy;

        if (newMinX > newMaxX || newMinY > newMaxY) {
            return BoundingBox(); // Invalid after deflation
        }
        return BoundingBox(newMinX, newMinY, newMaxX, newMaxY);
    }

    BoundingBox BoundingBox::alignToGrid(float gridSize) const {
        if (!m_IsValid) return BoundingBox();
        float alignedMinX = std::floor(m_Min.x / gridSize) * gridSize;
        float alignedMinY = std::floor(m_Min.y / gridSize) * gridSize;
        float alignedMaxX = std::ceil(m_Max.x / gridSize) * gridSize;
        float alignedMaxY = std::ceil(m_Max.y / gridSize) * gridSize;
        return BoundingBox(alignedMinX, alignedMinY, alignedMaxX, alignedMaxY);
    }

    float BoundingBox::distanceTo(float x, float y) const {
        if (!m_IsValid) return std::numeric_limits<float>::max();
        if (contains(x, y)) return 0.0f;

        float dx = std::max({ m_Min.x - x, 0.0f, x - m_Max.x });
        float dy = std::max({ m_Min.y - y, 0.0f, y - m_Max.y });

        return std::sqrt(dx * dx + dy * dy);
    }

    float BoundingBox::distanceTo(const glm::vec2& point) const {
        return distanceTo(point.x, point.y);
    }

    // ==================== 获取 ====================

    float BoundingBox::getWidth() const {
        return m_IsValid ? m_Max.x - m_Min.x : 0.0f;
    }

    float BoundingBox::getHeight() const {
        return m_IsValid ? m_Max.y - m_Min.y : 0.0f;
    }

    // ==================== 对象方法 ====================

    BoundingBox BoundingBox::copy() const {
        return BoundingBox(*this);
    }

    std::string BoundingBox::toString() const {
        if (!m_IsValid) {
            return "BoundingBox{INVALID}";
        }
        std::stringstream ss;
        ss << std::fixed << std::setprecision(2)
            << "BoundingBox{min=(" << m_Min.x << ", " << m_Min.y
            << "), max=(" << m_Max.x << ", " << m_Max.y
            << "), size=(" << getWidth() << ", " << getHeight() << ")}";
        return ss.str();
    }

    bool BoundingBox::operator==(const BoundingBox& other) const {
        if (m_IsValid != other.m_IsValid) return false;
        if (!m_IsValid) return true; // Two invalid boxes are considered equal
        return m_Min == other.m_Min && m_Max == other.m_Max;
    }

    bool BoundingBox::operator!=(const BoundingBox& other) const {
        return !(*this == other);
    }

    // ==================== 静态工厂方法 ====================

    BoundingBox BoundingBox::fromPoints(const std::vector<glm::vec2>& points) {
        return BoundingBox(points);
    }

    BoundingBox BoundingBox::fromVertices(const std::vector<float>& vertices) {
        return BoundingBox(vertices);
    }

    BoundingBox BoundingBox::merge(const BoundingBox& box1, const BoundingBox& box2) {
        return box1.getUnion(box2);
    }

    BoundingBox BoundingBox::mergeAll(const std::vector<BoundingBox>& boxes) {
        BoundingBox result;
        for (const auto& box : boxes) {
            result.expand(box);
        }
        return result;
    }

} // namespace Vivid2D::util