#include "legospline.h"

#include "mxgeometry/mxmatrix.h"

DECOMP_SIZE_ASSERT(LegoSpline, 0x50)

// FUNCTION: LEGO1 0x1009a0f0
LegoSpline::LegoSpline()
{
	for (LegoS32 i = 0; i < sizeOfArray(m_coefficents); i++) {
		m_coefficents[i].Clear();
	}
}

// FUNCTION: LEGO1 0x1009a130
LegoSpline::~LegoSpline()
{
}

// FUNCTION: LEGO1 0x1009a140
// FUNCTION: BETA10 0x10182c2f
void LegoSpline::SetSpline(
	const Vector3& p_start,
	const Vector3& p_tangentAtStart,
	const Vector3& p_end,
	const Vector3& p_tangentAtEnd
)
{
	m_coefficents[0] = p_start;
	m_coefficents[1] = p_tangentAtStart;

	for (LegoS32 i = 0; i < 3; i++) {
		m_coefficents[2][i] = (p_end[i] - p_start[i]) * 3.0f - p_tangentAtStart[i] * 2.0f - p_tangentAtEnd[i];
		m_coefficents[3][i] = (p_start[i] - p_end[i]) * 2.0f + p_tangentAtEnd[i] + p_tangentAtStart[i];
	}
}

// FUNCTION: LEGO1 0x1009a1e0
// FUNCTION: BETA10 0x10182d61
LegoResult LegoSpline::Evaluate(float p_alpha, Matrix4& p_mat, Vector3& p_up, LegoU32 p_reverse)
{
	Vector3 position(p_mat[3]);
	Vector3 right(p_mat[0]);
	Vector3 up(p_mat[1]);
	Vector3 dir(p_mat[2]);

	if (isnan(p_alpha) || p_alpha <= 0.001) {
		position = m_coefficents[0];
		dir = m_coefficents[1];
	}
	else if (p_alpha >= 0.999) {
		position = m_coefficents[0];
		position += m_coefficents[1];
		position += m_coefficents[2];
		position += m_coefficents[3];

		for (LegoS32 i = 0; i < 3; i++) {
			dir[i] = m_coefficents[1][i] + m_coefficents[2][i] * 2.0f + m_coefficents[3][i] * 3.0f;
		}
	}
	else {
		float alpha_squared = p_alpha * p_alpha;
		float alpha_cubed = alpha_squared * p_alpha;

		for (LegoS32 i = 0; i < 3; i++) {
			position[i] = m_coefficents[0][i] + m_coefficents[1][i] * p_alpha + m_coefficents[2][i] * alpha_squared +
						  m_coefficents[3][i] * alpha_cubed;
			dir[i] =
				m_coefficents[1][i] + m_coefficents[2][i] * p_alpha * 2.0f + m_coefficents[3][i] * alpha_squared * 3.0f;
		}
	}

	if (p_reverse) {
		dir *= -1.0f;
	}

	if (dir.Unitize() != 0) {
		return FAILURE;
	}

	right.EqualsCross(p_up, dir);
	if (right.Unitize() != 0) {
		return FAILURE;
	}

	up.EqualsCross(dir, right);
	return SUCCESS;
}
