#include "input/human_curve.hpp"
#include <cmath>
#include <chrono>
#include <algorithm>

HumanCurveGenerator::HumanCurveGenerator()
    : m_rng(static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())),
      m_jitterDist(0.0f, 0.6f),
      m_dwellDist(75.0f, 15.0f) {}

Vector2D HumanCurveGenerator::CalculateCubicBezier(float t, Vector2D p0, Vector2D p1, Vector2D p2, Vector2D p3) {
    float u = 1.0f - t;
    float tt = t * t;
    float uu = u * u;
    float uuu = uu * u;
    float ttt = tt * t;

    Vector2D p;
    p.x = uuu * p0.x + 3.0f * uu * t * p1.x + 3.0f * u * tt * p2.x + ttt * p3.x;
    p.y = uuu * p0.y + 3.0f * uu * t * p1.y + 3.0f * u * tt * p2.y + ttt * p3.y;
    return p;
}

void HumanCurveGenerator::GenerateTrajectory(
    Vector2D start,
    Vector2D target,
    common::TrajectoryBuffer& outBuf,
    int steps,
    bool enableOvershoot
) {
    outBuf.clear();
    if (steps < 2) steps = 2;
    if (steps > 30) steps = 30; // Giới hạn kích thước vừa vặn trong buffer 32 slots

    float dx = target.x - start.x;
    float dy = target.y - start.y;
    float dist = std::sqrt(dx * dx + dy * dy);

    // Tính điểm điều khiển P1, P2 lệch ngẫu nhiên so với đường thẳng
    std::uniform_real_distribution<float> devDist(-0.25f, 0.25f);
    float devMagnitude = std::min(dist * 0.35f, 120.0f);

    Vector2D p0 = start;
    Vector2D p3 = target;

    // Xử lý hiện tượng Overshoot (Lướt quá đích 35% xác suất)
    std::uniform_real_distribution<float> probDist(0.0f, 1.0f);
    bool doOvershoot = enableOvershoot && (probDist(m_rng) < 0.35f) && (dist > 60.0f);

    if (doOvershoot) {
        float overshootDist = std::uniform_real_distribution<float>(3.0f, 8.0f)(m_rng);
        p3.x += (dx / dist) * overshootDist;
        p3.y += (dy / dist) * overshootDist;
    }

    Vector2D p1;
    p1.x = start.x + dx * 0.3f + devDist(m_rng) * devMagnitude;
    p1.y = start.y + dy * 0.3f + devDist(m_rng) * devMagnitude;

    Vector2D p2;
    p2.x = start.x + dx * 0.7f + devDist(m_rng) * devMagnitude;
    p2.y = start.y + dy * 0.7f + devDist(m_rng) * devMagnitude;

    Vector2D currentPos = start;

    for (int i = 1; i <= steps; ++i) {
        // Tốc độ di chuyển tuân theo đường cong chữ S (Sigmoid / Ease-in-out theo định luật Fitts)
        float rawT = static_cast<float>(i) / static_cast<float>(steps);
        float t = (std::sin((rawT - 0.5f) * 3.14159265f) + 1.0f) * 0.5f;

        Vector2D nextPos = CalculateCubicBezier(t, p0, p1, p2, p3);

        // Bổ sung rung lắc sinh học ngẫu nhiên (Brownian micro-jitter)
        if (i < steps) {
            nextPos.x += m_jitterDist(m_rng);
            nextPos.y += m_jitterDist(m_rng);
        }

        short stepDx = static_cast<short>(std::round(nextPos.x - currentPos.x));
        short stepDy = static_cast<short>(std::round(nextPos.y - currentPos.y));

        outBuf.push_back({ static_cast<float>(stepDx), static_cast<float>(stepDy) });
        currentPos.x += stepDx;
        currentPos.y += stepDy;
    }

    // Nếu có Overshoot, bổ sung các bước căn chỉnh lại đúng tâm mục tiêu (Correction phase)
    if (doOvershoot) {
        short correctDx = static_cast<short>(std::round(target.x - currentPos.x));
        short correctDy = static_cast<short>(std::round(target.y - currentPos.y));
        outBuf.push_back({ static_cast<float>(correctDx), static_cast<float>(correctDy) });
    }
}

std::vector<Vector2D> HumanCurveGenerator::GenerateTrajectory(
    Vector2D start,
    Vector2D target,
    int steps,
    bool enableOvershoot
) {
    common::TrajectoryBuffer buf;
    GenerateTrajectory(start, target, buf, steps, enableOvershoot);
    std::vector<Vector2D> relativeSteps;
    relativeSteps.reserve(buf.size());
    for (size_t i = 0; i < buf.size(); ++i) {
        relativeSteps.push_back(buf[i]);
    }
    return relativeSteps;
}

int HumanCurveGenerator::GenerateDwellTimeMs(float meanMs, float stdDevMs) {
    std::normal_distribution<float> dist(meanMs, stdDevMs);
    float val = dist(m_rng);
    return static_cast<int>(std::clamp(val, 35.0f, 250.0f));
}
