#pragma once

// ==========================================================
// math2d.hpp - 2D Vector & Isometric Geometry Library
// C++23 Core Engine - AutoPOE2 (DOC-49 & Rule 16)
// ==========================================================

#include <cmath>
#include <algorithm>

namespace common {

/**
 * @brief Cấu trúc Vector 2 chiều (2D Vector) hiệu năng cao, zero-allocation.
 * 
 * Đại diện cho vị trí hoặc vector hướng trong không gian thế giới (World Space),
 * lưới đồ họa (Grid Space) hoặc pixel màn hình (Screen Space).
 */
struct Vec2 {
    float x = 0.0f;
    float y = 0.0f;

    constexpr Vec2() noexcept = default;
    constexpr Vec2(float _x, float _y) noexcept : x(_x), y(_y) {}

    constexpr Vec2 operator+(const Vec2& o) const noexcept { return { x + o.x, y + o.y }; }
    constexpr Vec2 operator-(const Vec2& o) const noexcept { return { x - o.x, y - o.y }; }
    constexpr Vec2 operator*(float s) const noexcept { return { x * s, y * s }; }
    constexpr Vec2 operator/(float s) const noexcept {
        return (s != 0.0f) ? Vec2{ x / s, y / s } : Vec2{ 0.0f, 0.0f };
    }

    constexpr Vec2& operator+=(const Vec2& o) noexcept { x += o.x; y += o.y; return *this; }
    constexpr Vec2& operator-=(const Vec2& o) noexcept { x -= o.x; y -= o.y; return *this; }
    constexpr Vec2& operator*=(float s) noexcept { x *= s; y *= s; return *this; }
    constexpr Vec2& operator/=(float s) noexcept {
        if (s != 0.0f) { x /= s; y /= s; }
        return *this;
    }

    constexpr bool operator==(const Vec2& o) const noexcept = default;

    /**
     * @brief Tính độ dài (magnitude) của vector Euclidean.
     */
    [[nodiscard]] float Length() const noexcept {
        return std::sqrt(x * x + y * y);
    }

    /**
     * @brief Tính bình phương độ dài vector (tránh căn bậc hai tốn kém khi chỉ so sánh).
     */
    [[nodiscard]] constexpr float LengthSq() const noexcept {
        return x * x + y * y;
    }

    /**
     * @brief Tính khoảng cách Euclidean giữa 2 điểm.
     */
    [[nodiscard]] float Distance(const Vec2& o) const noexcept {
        float dx = x - o.x;
        float dy = y - o.y;
        return std::sqrt(dx * dx + dy * dy);
    }

    /**
     * @brief Tính bình phương khoảng cách giữa 2 điểm.
     */
    [[nodiscard]] constexpr float DistanceSq(const Vec2& o) const noexcept {
        float dx = x - o.x;
        float dy = y - o.y;
        return dx * dx + dy * dy;
    }

    /**
     * @brief Chuẩn hóa vector về vector đơn vị (unit vector với length = 1.0f).
     * @return Vec2 Vector đơn vị hoặc (0,0) nếu độ dài ban đầu xấp xỉ 0.
     */
    [[nodiscard]] Vec2 Normalized() const noexcept {
        float len = Length();
        return (len > 0.0001f) ? (*this / len) : Vec2{};
    }

    /**
     * @brief Tích vô hướng (Dot product) giữa 2 vector.
     */
    [[nodiscard]] constexpr float Dot(const Vec2& o) const noexcept {
        return x * o.x + y * o.y;
    }

    /**
     * @brief Phép nội suy tuyến tính (Linear Interpolation) giữa 2 vector.
     * @param a Điểm bắt đầu (t = 0).
     * @param b Điểm kết thúc (t = 1).
     * @param t Hệ số nội suy trong khoảng [0, 1].
     */
    [[nodiscard]] static constexpr Vec2 Lerp(const Vec2& a, const Vec2& b, float t) noexcept {
        return a + (b - a) * std::clamp(t, 0.0f, 1.0f);
    }
};

/**
 * @brief Điểm trên tọa độ màn hình pixel 2D.
 */
struct ScreenPoint {
    float x{0.0f};
    float y{0.0f};
};

/**
 * @brief Trạng thái phím điều hướng WASD (8 hướng Isometric).
 */
struct WasdVector {
    bool up{false};
    bool down{false};
    bool left{false};
    bool right{false};

    constexpr bool operator==(const WasdVector& other) const noexcept = default;
    constexpr bool empty() const noexcept {
        return !up && !down && !left && !right;
    }
};

/**
 * @brief Chuẩn hóa vector delta thế giới (dx, dy) sang tổ hợp phím WASD theo hệ tọa độ Isometric POE2.
 * 
 * Hệ trục POE2 Isometric:
 * - ndx, ndy: vector đơn vị hướng di chuyển trong không gian thế giới
 * - screenX = (ndx - ndy) * 0.70710678f
 * - screenY = (ndx + ndy) * 0.70710678f
 * 
 * Ngưỡng kThreshold = 0.38f (~sin(22.5 độ)) phân định chính xác 8 góc 45 độ:
 * - W (Lên / Đông-Bắc): screenY > kThreshold
 * - S (Xuống / Tây-Nam): screenY < -kThreshold
 * - D (Phải / Đông-Nam): screenX > kThreshold
 * - A (Trái / Tây-Bắc): screenX < -kThreshold
 * - Tổ hợp chéo: W+D, W+A, S+D, S+A khi cả 2 trục vượt ngưỡng kThreshold
 * 
 * @param dx Độ lệch trục X thế giới
 * @param dy Độ lệch trục Y thế giới
 * @param deadzone Bán kính vùng chết (đơn vị thế giới)
 * @return WasdVector Trạng thái 4 phím W, A, S, D
 */
inline WasdVector WorldToWasd(float dx, float dy, float deadzone = 0.01f) {
    const float len = std::sqrt(dx * dx + dy * dy);
    if (len <= 0.0001f || len < deadzone) {
        return WasdVector{};
    }

    const float ndx = dx / len;
    const float ndy = dy / len;

    constexpr float kInvSqrt2 = 0.70710678f;
    const float screenX = (ndx - ndy) * kInvSqrt2;
    const float screenY = (ndx + ndy) * kInvSqrt2;

    constexpr float kThreshold = 0.38f;

    WasdVector wasd;
    if (screenY > kThreshold) wasd.up = true;
    else if (screenY < -kThreshold) wasd.down = true;

    if (screenX > kThreshold) wasd.right = true;
    else if (screenX < -kThreshold) wasd.left = true;

    return wasd;
}

/**
 * @brief Chuẩn hóa phép chiếu Isometric 2D từ vector delta thế giới (dx, dy) sang tọa độ màn hình pixel POE2.
 * 
 * Hệ trục màn hình Windows:
 * - X tăng dần sang phải màn hình.
 * - Y tăng dần xuống dưới màn hình (cy là tâm màn hình).
 * 
 * Phép chiếu Isometric POE2:
 * - Trục X màn hình: targetX = cx + (ndx - ndy) * dist * 0.707f
 * - Trục Y màn hình: targetY = cy - (ndx + ndy) * dist * 0.45f
 *   (Khi nhân vật tiến lên phía trước/bắc thế giới: ndx + ndy > 0 -> targetY < cy, tức hướng lên nửa trên màn hình).
 * 
 * @param dx Độ lệch trục X thế giới (target.x - origin.x)
 * @param dy Độ lệch trục Y thế giới (target.y - origin.y)
 * @param dist Khoảng cách chiếu pixel từ tâm
 * @param screenW Chiều rộng màn hình (pixel)
 * @param screenH Chiều cao màn hình (pixel)
 * @param margin Khoảng cách an toàn tối thiểu tính từ rìa màn hình
 * @return ScreenPoint Tọa độ pixel trên màn hình đã được clamp an toàn
 */
inline ScreenPoint WorldToScreenIsometric(float dx, float dy, float dist, float screenW, float screenH, float margin = 50.0f) {
    if (screenW <= 0.0f) screenW = 1920.0f;
    if (screenH <= 0.0f) screenH = 1080.0f;

    const float cx = screenW * 0.5f;
    const float cy = screenH * 0.5f;

    float ndx = 0.0f;
    float ndy = 0.0f;
    const float len = std::sqrt(dx * dx + dy * dy);
    if (len > 0.001f) {
        ndx = dx / len;
        ndy = dy / len;
    }

    float targetX = cx + (ndx - ndy) * dist * 0.707f;
    float targetY = cy - (ndx + ndy) * dist * 0.45f;

    const float minX = (std::min)(margin, cx);
    const float maxX = (std::max)(cx, screenW - margin);
    const float minY = (std::min)(margin, cy);
    const float maxY = (std::max)(cy, screenH - margin);

    targetX = (std::max)(minX, (std::min)(targetX, maxX));
    targetY = (std::max)(minY, (std::min)(targetY, maxY));

    return { targetX, targetY };
}

/**
 * @brief Phiên bản tiện ích tự động tính cự ly khoảng cách pixel từ độ dài vector (dx, dy).
 */
inline ScreenPoint WorldToScreenIsometric(float dx, float dy, float screenW, float screenH, float margin = 50.0f) {
    const float dist = std::sqrt(dx * dx + dy * dy);
    return WorldToScreenIsometric(dx, dy, dist, screenW, screenH, margin);
}

} // namespace common
