#pragma once

#ifndef __mathhelper_h__
	#define __mathhelper_h__

#include "foundation.h"
#include "foundation/PxMat44.h"

using namespace physx;

namespace Gx
{
	static const PxReal PiDiv180 = PxReal(0.017453292519943);

	class GX_API MathHelper
	{
	public:
		static PxMat44 createIdentity() {
			return PxMat44::createIdentity();
		}

		static PxMat44 createTranslation(const PxVec3& t) {
			PxMat44 m = PxMat44::createIdentity();
			m.column3 = PxVec4(t, 1);
			return m;
		}

		static PxMat44 createTranslation(PxReal x, PxReal y, PxReal z) {
			PxMat44 m = PxMat44::createIdentity();
			m.column3 = PxVec4(x, y, z, 1);
			return m;
		}

		static PxMat44 createScale(PxReal s) {
			return PxMat44(PxVec4(s, s, s, 1));
		}

		static PxMat44 createScale(PxReal x, PxReal y, PxReal z) {
			return PxMat44(PxVec4(x, y, z, 1));
		}

		static PxMat44 createScale(const PxVec3& s) {
			return PxMat44(PxVec4(s, 1));
		}

		static PxMat44 createRotationX(PxReal angle) {
			angle = angle * PiDiv180; // to radians
			const PxReal s = PxSin(angle);
			const PxReal c = PxCos(angle);

			PxMat44 rot = PxMat44(PxVec4(1, c, c, 1));
			rot.column1.z = s; rot.column2.y = -s;

			return rot;
		}

		static PxMat44 createRotationY(PxReal angle) {
			angle = angle * PiDiv180; // to radians
			const PxReal s = PxSin(angle);
			const PxReal c = PxCos(angle);

			PxMat44 rot = PxMat44(PxVec4(c, 1, c, 1));
			rot.column0.z = -s; rot.column2.x = s;

			return rot;
		}

		static PxMat44 createRotationZ(PxReal angle) {
			angle = angle * PiDiv180; // to radians
			const PxReal s = PxSin(angle);
			const PxReal c = PxCos(angle);

			PxMat44 rot = PxMat44(PxVec4(c, c, 1, 1));
			rot.column0.y = s; rot.column1.x = -s;

			return rot;
		}

		static PxMat44 createRotation(PxReal angle, PxReal x, PxReal y, PxReal z) {
			angle *= PiDiv180; // to radians

			PxReal magnitude = PxSqrt(x*x + y*y + z*z);

			if ( magnitude == 0 )
				return PxMat44::createIdentity();

			const PxReal s = PxSin(angle);
			const PxReal c = PxCos(angle);
			const PxReal t = 1 - c;

			const PxReal sx = s * x;
			const PxReal sy = s * y;
			const PxReal sz = s * z;

			return PxMat44(
				PxVec4(t * (x*x) + c, t * (x*y) + sz, t * (x*z) - sy, 0),
				PxVec4(t * (x*y) - sz, t * (y*y) + c, t * (y*z) + sx, 0),
				PxVec4(t * (x*z) + sy, t * (y*z) - sx, t * (z*z) + c, 0),
				PxVec4(0, 0, 0, 1));
		}

		static PxMat44 createRotation(PxReal angle, const PxVec3& v) {
			return createRotation(angle, v.x, v.y, v.z);
		}

		static PxMat44 createPerspective(PxReal fovy, PxReal aspect, PxReal zMin, PxReal zMax) {
			const PxReal yMax = zMin * PxTan(fovy * PiDiv180 * PxReal(0.5));
			const PxReal yMin = -yMax;
			const PxReal xMin = yMin * aspect;
			const PxReal xMax = -xMin;

			return PxMat44(
				PxVec4((2*zMin) / (xMax-xMin), 0, 0, 0),
				PxVec4(0, (2*zMin) / (yMax-yMin), 0, 0),
				PxVec4((xMax+xMin) / (xMax-xMin), (yMax+yMin) / (yMax - yMin), -((zMax+zMin) / (zMax-zMin)), -1),
				PxVec4(0, 0, -((2*(zMax*zMin)) / (zMax - zMin)), 0));
		}

		static PxMat44 createOrthographic(PxReal xMin, PxReal xMax, PxReal yMin, PxReal yMax, PxReal zMin, PxReal zMax) {
			return PxMat44(
				PxVec4(PxReal(2) / (xMax-xMin), 0, 0, 0),
				PxVec4(0, PxReal(2) / (yMax-yMin), 0, 0),
				PxVec4(0, 0, PxReal(-2) / (zMax-zMin), 0),
				PxVec4(-((xMax+xMin)/(xMax-xMin)), -((yMax+yMin)/(yMax-yMin)), -((zMax+zMin)/(zMax-zMin)), 1));
		}
	};
}

#endif /* __mathhelper_h__ */