/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmAngle.h"
#include "glmQuaternion.h"
#include "glmMathBase.h"
#include "glmMatrix4.h"

namespace glm
{

	//----------------------------------------------------------------------------
	// scalar interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolate(float & result, float value1, float value2, float factor)
	{
		result = value1 + (value2 - value1) * factor;
	}

	inline float GLM_CORE_API interpolate(float value1, float value2, float factor)
	{
		float result;
		interpolate(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// vector 2 linear interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolate(Vector2 & result, const Vector2 & value1, const Vector2 & value2, float factor)
	{
		result.setValues(value1.x + (value2.x - value1.x) * factor,
			value1.y + (value2.y - value1.y) * factor);
	}

	inline Vector2 GLM_CORE_API interpolate(const Vector2 & value1, const Vector2 & value2, float factor)
	{
		Vector2 result;
		interpolate(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// vector 3 linear interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolate(Vector3 & result, const Vector3 & value1, const Vector3 & value2, float factor)
	{
		result.setValues(value1.x + (value2.x - value1.x) * factor,
			value1.y + (value2.y - value1.y) * factor,
			value1.z + (value2.z - value1.z) * factor);
	}

	inline Vector3 GLM_CORE_API interpolate(const Vector3 & value1, const Vector3 & value2, float factor)
	{
		Vector3 result;
		interpolate(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// float[] linear interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolate(float* result, const float* value1, const float* value2, size_t arraySize, float factor)
	{
		for(size_t i=0; i< arraySize; ++i)
			result[i] = value1[i] + (value2[i] - value1[i]) * factor;
	}

	//----------------------------------------------------------------------------
	// vector 4 linear interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolate(Vector4 & result, const Vector4 & value1, const Vector4 & value2, float factor)
	{
		result.setValues(value1.x + (value2.x - value1.x) * factor,
			value1.y + (value2.y - value1.y) * factor,
			value1.z + (value2.z - value1.z) * factor,
			value1.w + (value2.w - value1.w) * factor);
	}

	inline Vector4 GLM_CORE_API interpolate(const Vector4 & value1, const Vector4 & value2, float factor)
	{
		Vector4 result;
		interpolate(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// angle linear interpolation
	//----------------------------------------------------------------------------
	inline void GLM_CORE_API interpolateAngle(Angle & result, Angle value1, Angle value2, float factor)
	{
		result = value1 + AngleUtils::getModuloValue((value2 - value1))*factor;
	}

	inline Angle GLM_CORE_API interpolateAngle(Angle value1, Angle value2, float factor)
	{
		Angle result;
		interpolateAngle(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// Quaternion interpolation
	//----------------------------------------------------------------------------
	void GLM_CORE_API interpolate_noFactorRangeCheck(Quaternion & result, const Quaternion & value1, const Quaternion & value2, float factor);	//in some, factor can be outside the range without it being a problem. But in most cases, the factor range check saved us from bugs, so let's keep it by default.

	inline void GLM_CORE_API interpolate(Quaternion & result, const Quaternion & value1, const Quaternion & value2, float factor)
	{
		GLM_DEBUG_ASSERT_MSG(((factor <= 1.) && (factor >= 0.)), "factor should be comprised between 0 and 1");
		interpolate_noFactorRangeCheck(result, value1, value2, factor);
	}

	inline Quaternion GLM_CORE_API interpolate(const Quaternion & value1, const Quaternion & value2, float factor)
	{
		Quaternion result;
		interpolate(result, value1, value2, factor);
		return result;
	}

	//----------------------------------------------------------------------------
	// Matrix4 interpolate
	//----------------------------------------------------------------------------
	inline Matrix4 GLM_CORE_API interpolate(const Matrix4 & value1, const Matrix4 & value2, float factor)
	{
		Matrix4 result;
		for (int i = 0; i<4; i++)
		{
			for (int j = 0; j<4; j++)
			{
				result[i][j] = interpolate(value1[i][j], value2[i][j], factor);
			}
		}
		return result;
	}

	//------------------------------------------------------------
	inline void interpolate(Matrix4& result, const Matrix4& value1, const Matrix4& value2, float factor)
	{
		for (int i = 0; i<4; ++i)
		{
			for (int j = 0; j<4; ++j)
			{
				interpolate(result[i][j], value1[i][j], value2[i][j], factor);
			}
		}
	}

	//----------------------------------------------------------------------------
	// smootherstep
	//----------------------------------------------------------------------------
	inline float smootherstep(float edge0, float edge1, float x)
	{
		// Scale, and saturate x to 0..1 range
		x = clamp((x - edge0) / (edge1 - edge0), 0.f, 1.f);
		// Evaluate polynomial
		return x * x*x*(x*(x * 6 - 15) + 10);
	}
}

