#include "Project/Rail/LinearCurve.h"

#include "Library/Math/MathUtil.h"

namespace al {

LinearCurve::LinearCurve() = default;

void LinearCurve::set(const sead::Vector3f& start, const sead::Vector3f& end) {
    mStart = start;
    mDiff.x = end.x - start.x;
    mDiff.y = end.y - start.y;
    mDiff.z = end.z - start.z;
    mDistance = sead::Mathf::sqrt(mDiff.x * mDiff.x + mDiff.y * mDiff.y + mDiff.z * mDiff.z);
}

void LinearCurve::calcPos(sead::Vector3f* pos, f32 param) const {
    pos->x = (mDiff.x * param) + mStart.x;
    pos->y = (mDiff.y * param) + mStart.y;
    pos->z = (mDiff.z * param) + mStart.z;
}

void LinearCurve::calcVelocity(sead::Vector3f* vel, f32 param) const {
    *vel = mDiff;
}

f32 LinearCurve::calcLength(f32 param_start, f32 param_end) const {
    return mDistance * sead::Mathf::abs(param_end - param_start);
}

f32 LinearCurve::calcCurveParam(f32 param) const {
    if (isNearZero(mDistance, 0.001))
        return 0;

    return sead::Mathf::clamp(param, 0, mDistance) / mDistance;
}

f32 LinearCurve::calcNearestParam(const sead::Vector3f& pos) const {
    if (isNearZero(mDistance, 0.001))
        return 0;

    f32 dot = (pos - mStart).dot(mDiff);
    return sead::Mathf::clamp(dot / sead::Mathf::square(mDistance), 0, 1);
}

f32 LinearCurve::calcNearestLength(f32* length, const sead::Vector3f& pos, f32 param) const {
    f32 nearestParam = calcNearestParam(pos);

    sead::Vector3f nearestPos;
    calcPos(&nearestPos, nearestParam);
    sead::Vector3f diff = nearestPos - pos;
    f32 len = diff.squaredLength();

    *length = nearestParam * param;
    return len;
}

// NON_MATCHING: Difference in loading for calcNearestParam (https://decomp.me/scratch/u0H2R)
void LinearCurve::calcNearestPos(sead::Vector3f* nearest, const sead::Vector3f& pos) const {
    calcPos(nearest, calcNearestParam(pos));
}

void LinearCurve::calcStartPos(sead::Vector3f* pos) const {
    *pos = mStart;
}

void LinearCurve::calcEndPos(sead::Vector3f* pos) const {
    pos->x = mStart.x + mDiff.x;
    pos->y = mStart.y + mDiff.y;
    pos->z = mStart.z + mDiff.z;
}

}  // namespace al
