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

namespace input {

// Bảng hoán vị 512 phần tử cho Ken Perlin noise
const std::array<uint8_t, 512> QuinticBezierController::s_perlinPermutation = {
    151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
    8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
    35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
    134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
    55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
    18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
    250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
    189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
    172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
    228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,
    107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
    // Nhân bản 256 phần tử để tránh tràn chỉ số khi tra cứu X + 1
    151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
    8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
    35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
    134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
    55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
    18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
    250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
    189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
    172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
    228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,
    107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

QuinticBezierController::QuinticBezierController(const QuinticBezierConfig& config)
    : m_config(config),
      m_rng(static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())),
      m_gaussianDist(0.0f, config.gaussianSigma),
      m_uniformDist(0.0f, 1.0f) {}

float QuinticBezierController::EvaluateMinimumJerk(float tau) {
    if (tau <= 0.0f) return 0.0f;
    if (tau >= 1.0f) return 1.0f;
    // s(tau) = 10*tau^3 - 15*tau^4 + 6*tau^5
    const float tau3 = tau * tau * tau;
    return tau3 * (10.0f + tau * (-15.0f + 6.0f * tau));
}

Vector2D QuinticBezierController::EvaluateQuinticBezier(
    float t,
    Vector2D p0, Vector2D p1, Vector2D p2,
    Vector2D p3, Vector2D p4, Vector2D p5
) {
    const float u = 1.0f - t;
    const float u2 = u * u;
    const float u3 = u2 * u;
    const float u4 = u3 * u;
    const float u5 = u4 * u;

    const float t2 = t * t;
    const float t3 = t2 * t;
    const float t4 = t3 * t;
    const float t5 = t4 * t;

    // Các đa thức Bernstein bậc 5
    const float b0 = u5;
    const float b1 = 5.0f * u4 * t;
    const float b2 = 10.0f * u3 * t2;
    const float b3 = 10.0f * u2 * t3;
    const float b4 = 5.0f * u * t4;
    const float b5 = t5;

    Vector2D pt;
    pt.x = b0 * p0.x + b1 * p1.x + b2 * p2.x + b3 * p3.x + b4 * p4.x + b5 * p5.x;
    pt.y = b0 * p0.y + b1 * p1.y + b2 * p2.y + b3 * p3.y + b4 * p4.y + b5 * p5.y;
    return pt;
}

float QuinticBezierController::PerlinNoise1D(float x) {
    const int xi = static_cast<int>(std::floor(x)) & 255;
    const float xf = x - std::floor(x);

    // Quintic smootherstep fade curve
    const float u = xf * xf * xf * (xf * (xf * 6.0f - 15.0f) + 10.0f);

    const int g0 = (s_perlinPermutation[xi] & 1) ? 1 : -1;
    const int g1 = (s_perlinPermutation[xi + 1] & 1) ? 1 : -1;

    const float d0 = static_cast<float>(g0) * xf;
    const float d1 = static_cast<float>(g1) * (xf - 1.0f);

    return (1.0f - u) * d0 + u * d1;
}

void QuinticBezierController::GenerateTrajectory(
    Vector2D start,
    Vector2D target,
    common::TrajectoryBuffer& outBuf,
    int steps
) {
    outBuf.clear();

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

    if (dist < 1.0f) {
        return;
    }

    // Tự động tính số bước theo Định luật Fitts nếu không truyền tham số
    int nSteps = steps;
    if (nSteps <= 0) {
        const float fittsSteps = 10.0f + 3.0f * std::log2(1.0f + dist / 20.0f);
        nSteps = static_cast<int>(std::clamp(fittsSteps, 14.0f, 26.0f));
    }
    nSteps = std::clamp(nSteps, 4, 30); // Giới hạn tối đa 30 bước để chừa slot cho overshoot correction

    // Vector pháp tuyến ngang của hướng đi
    const float invDist = 1.0f / dist;
    const float normX = -dy * invDist;
    const float normY = dx * invDist;

    // Chiều uốn cong sinh học của cổ tay (Lateral curvature)
    const float side = (m_uniformDist(m_rng) < 0.5f) ? -1.0f : 1.0f;
    const float lateralDev = std::min(dist * m_config.curvatureVariance, m_config.maxLateralDeviation) * side;

    Vector2D p0 = start;
    Vector2D p5 = target;

    // Hiện tượng lướt quá đà sinh học (Overshoot)
    const bool doOvershoot = (dist >= m_config.overshootMinDist) &&
                             (m_uniformDist(m_rng) < m_config.overshootProbability);
    if (doOvershoot) {
        const float overshootMag = 3.0f + m_uniformDist(m_rng) * 5.0f; // 3 - 8 pixels
        p5.x += (dx * invDist) * overshootMag;
        p5.y += (dy * invDist) * overshootMag;
    }

    // Thiết lập 4 điểm điều khiển trung gian của Quintic Bézier
    // Theo hình thái tự nhiên của cánh tay người (Arc + S-shape curvature)
    std::uniform_real_distribution<float> jitterDist(-0.10f, 0.10f);

    Vector2D p1;
    p1.x = start.x + dx * 0.18f + normX * (lateralDev * (0.40f + jitterDist(m_rng)));
    p1.y = start.y + dy * 0.18f + normY * (lateralDev * (0.40f + jitterDist(m_rng)));

    Vector2D p2;
    p2.x = start.x + dx * 0.40f + normX * (lateralDev * (0.90f + jitterDist(m_rng)));
    p2.y = start.y + dy * 0.40f + normY * (lateralDev * (0.90f + jitterDist(m_rng)));

    Vector2D p3;
    p3.x = start.x + dx * 0.65f + normX * (lateralDev * (0.85f + jitterDist(m_rng)));
    p3.y = start.y + dy * 0.65f + normY * (lateralDev * (0.85f + jitterDist(m_rng)));

    Vector2D p4;
    p4.x = start.x + dx * 0.88f + normX * (lateralDev * (0.35f + jitterDist(m_rng)));
    p4.y = start.y + dy * 0.88f + normY * (lateralDev * (0.35f + jitterDist(m_rng)));

    // Seed ngẫu nhiên cho sóng Perlin để mỗi đường di chuột có phổ vi rung độc bản
    const float seedX = m_uniformDist(m_rng) * 100.0f;
    const float seedY = m_uniformDist(m_rng) * 100.0f;

    Vector2D currentPos = start;

    for (int i = 1; i <= nSteps; ++i) {
        const float tau = static_cast<float>(i) / static_cast<float>(nSteps);
        const float u = EvaluateMinimumJerk(tau);

        Vector2D nextPos = EvaluateQuinticBezier(u, p0, p1, p2, p3, p4, p5);

        // Điều chế biên độ rung theo vận tốc (động học cơ bắp)
        // Khi vung chuột nhanh ở giữa quỹ đạo (tau ~ 0.5), cơ siết chặt -> rung giảm
        // Khi hãm tốc gần đích (tau > 0.8), cơ đối kháng hoạt động -> vi rung tăng nhẹ
        const float vFactor = 16.0f * tau * tau * (1.0f - tau) * (1.0f - tau); // Đạt đỉnh 1.0 tại tau=0.5
        const float tremorMod = (1.0f - 0.65f * vFactor);

        if (i < nSteps) {
            if (m_config.enablePerlinTremor) {
                const float pTremorX = PerlinNoise1D(tau * m_config.tremorFrequency + seedX) * m_config.tremorAmplitude * tremorMod;
                const float pTremorY = PerlinNoise1D(tau * m_config.tremorFrequency + seedY) * m_config.tremorAmplitude * tremorMod;
                nextPos.x += pTremorX;
                nextPos.y += pTremorY;
            }

            if (m_config.enableGaussianJitter) {
                nextPos.x += m_gaussianDist(m_rng) * tremorMod;
                nextPos.y += m_gaussianDist(m_rng) * tremorMod;
            }
        }

        const short stepDx = static_cast<short>(std::round(nextPos.x - currentPos.x));
        const 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 bước căn chỉnh đích (Target Homing / Correction Phase)
    if (doOvershoot) {
        const short corrDx = static_cast<short>(std::round(target.x - currentPos.x));
        const short corrDy = static_cast<short>(std::round(target.y - currentPos.y));
        if (corrDx != 0 || corrDy != 0) {
            outBuf.push_back({ static_cast<float>(corrDx), static_cast<float>(corrDy) });
            currentPos.x += corrDx;
            currentPos.y += corrDy;
        }
    } else {
        // Đảm bảo không lệch tích lũy làm tròn: bước cuối bù chuẩn xác về target
        const short residualX = static_cast<short>(std::round(target.x - currentPos.x));
        const short residualY = static_cast<short>(std::round(target.y - currentPos.y));
        if (residualX != 0 || residualY != 0) {
            if (outBuf.size() > 0) {
                outBuf[outBuf.size() - 1].x += residualX;
                outBuf[outBuf.size() - 1].y += residualY;
            } else {
                outBuf.push_back({ static_cast<float>(residualX), static_cast<float>(residualY) });
            }
        }
    }
}

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

} // namespace input
