// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#ifndef FENNEC_MATH_EXT_COMMON_H
#define FENNEC_MATH_EXT_COMMON_H

#include <fennec/lang/limits.h>

#include <fennec/math/common.h>
#include <fennec/math/trigonometric.h>

#include <fennec/math/ext/quaternion.h>

namespace fennec
{

// Sign Functions ======================================================================================================

template<typename genType>
constexpr qua<genType> sign(const qua<genType>& x) {
	return qua<genType>(
		fennec::sign(x.w),
		fennec::sign(x.x),
		fennec::sign(x.y),
		fennec::sign(x.z)
	);
}

template<typename genType>
constexpr qua<genType> abs(const qua<genType>& x) {
	return qua<genType>(
		x.w * fennec::sign(x.w),
		x.x * fennec::sign(x.x),
		x.y * fennec::sign(x.y),
		x.z * fennec::sign(x.z)
	);
}


// Rounding Functions ==================================================================================================

template<typename genType>
constexpr qua<genType> floor(const qua<genType>& x) {
	return qua<genType>(
		fennec::floor(x.w),
		fennec::floor(x.x),
		fennec::floor(x.y),
		fennec::floor(x.z)
	);
}

template<typename genType>
constexpr qua<genType> ceil(const qua<genType>& x) {
	return qua<genType>(
		fennec::ceil(x.w),
		fennec::ceil(x.x),
		fennec::ceil(x.y),
		fennec::ceil(x.z)
	);
}

template<typename genType>
constexpr qua<genType> trunc(const qua<genType>& x) {
	return qua<genType>(
		fennec::trunc(x.w),
		fennec::trunc(x.x),
		fennec::trunc(x.y),
		fennec::trunc(x.z)
	);
}

template<typename genType>
constexpr qua<genType> round(const qua<genType>& x) {
	return qua<genType>(
		fennec::round(x.w),
		fennec::round(x.x),
		fennec::round(x.y),
		fennec::round(x.z)
	);
}

template<typename genType>
constexpr qua<genType> roundEven(const qua<genType>& x) {
	return qua<genType>(
		fennec::roundEven(x.w),
		fennec::roundEven(x.x),
		fennec::roundEven(x.y),
		fennec::roundEven(x.z)
	);
}

// Fractional Functions ================================================================================================

template<typename genType>
constexpr qua<genType> fract(const qua<genType>& x) {
	return qua<genType>(
		fennec::fract(x.w),
		fennec::fract(x.x),
		fennec::fract(x.y),
		fennec::fract(x.z)
	);
}

template<typename genType>
constexpr qua<genType> mod(const qua<genType>& x) {
	return qua<genType>(
		fennec::mod(x.w),
		fennec::mod(x.x),
		fennec::mod(x.y),
		fennec::mod(x.z)
	);
}

template<typename genType>
constexpr qua<genType> modf(const qua<genType>& x) {
	return qua<genType>(
		fennec::modf(x.w),
		fennec::modf(x.x),
		fennec::modf(x.y),
		fennec::modf(x.z)
	);
}

template<typename genType>
constexpr qua<genType> isnan(const qua<genType>& x) {
	return qua<genType>(
		fennec::isnan(x.w),
		fennec::isnan(x.x),
		fennec::isnan(x.y),
		fennec::isnan(x.z)
	);
}

template<typename genType>
constexpr qua<genType> isinf(const qua<genType>& x) {
	return qua<genType>(
		fennec::isinf(x.w),
		fennec::isinf(x.x),
		fennec::isinf(x.y),
		fennec::isinf(x.z)
	);
}

template<typename genType>
constexpr qua<genType> fma(const qua<genType>& a, const qua<genType>& b, const qua<genType>& c) {
	return qua<genType>(
		fennec::fma(a.w, b.w, c.w),
		fennec::fma(a.x, b.x, c.x),
		fennec::fma(a.y, b.y, c.y),
		fennec::fma(a.z, b.z, c.z)
	);
}


// Comparison Functions ================================================================================================

template<typename genType>
constexpr qua<genType> min(const qua<genType>& x, const qua<genType>& y) {
	return qua<genType>(
		fennec::min(x.w, y.w),
		fennec::min(x.x, y.x),
		fennec::min(x.y, y.y),
		fennec::min(x.z, y.z)
	);
}

template<typename genType>
constexpr qua<genType> max(const qua<genType>& x, const qua<genType>& y) {
	return qua<genType>(
		fennec::max(x.w, y.w),
		fennec::max(x.x, y.x),
		fennec::max(x.y, y.y),
		fennec::max(x.z, y.z)
	);
}

template<typename genType>
constexpr qua<genType> clamp(const qua<genType>& x, genType minVal, genType maxVal) {
	return qua<genType>(
		fennec::clamp(x.w, minVal, maxVal),
		fennec::clamp(x.x, minVal, maxVal),
		fennec::clamp(x.y, minVal, maxVal),
		fennec::clamp(x.z, minVal, maxVal)
	);
}

template<typename genType>
constexpr qua<genType> clamp(const qua<genType>& x, const qua<genType>& minVal, const qua<genType>& maxVal) {
	return qua<genType>(
		fennec::clamp(x.w, minVal.w, maxVal.w),
		fennec::clamp(x.x, minVal.x, maxVal.x),
		fennec::clamp(x.y, minVal.y, maxVal.y),
		fennec::clamp(x.z, minVal.z, maxVal.z)
	);
}


// Curves ==============================================================================================================

template<typename genType>
constexpr qua<genType> mix(const qua<genType>& x, const qua<genType>& y, genType a) {
	genType cT = fennec::dot(x, y);
	qua<genType> z = cT < genType(0) ? -y : y;
	cT = cT < genType(0) ? -cT : cT;

	if (cT > genType(1) - numeric_limits<genType>::epsilon()) {
		return x * (genType(1) - a) + y * a;
	}

	genType t = fennec::acos(cT);
	return (fennec::sin(x * (genType(1) - a) * t) + z * (fennec::sin(a * t))) / fennec::sin(t);
}

}


#endif // FENNEC_MATH_EXT_COMMON_H
