#pragma once

#include "affnmtrx.h"
#include "unitvec.h"
#include "rotation.h"

class LinearMatrix : public AffineMatrix
{
public:
	static const LinearMatrix Identity;

	LinearMatrix()		{ BuildIdentity(); }
	LinearMatrix(int)	{ BuildIdentity(); }

	LinearMatrix& operator=(const LinearMatrix &m)		{ AffineMatrix::operator=(m); return *this; }
	LinearMatrix& operator=(const Origin &p)			{ AffineMatrix::operator=(p); return *this; }
	LinearMatrix& operator=(const Hinge &hinge)			{ AffineMatrix::operator=(hinge); return *this; }
	LinearMatrix& operator=(const EulerAngles &angles)	{ AffineMatrix::operator=(angles); return *this; }
	LinearMatrix& operator=(const YawPitchRoll &angles)	{ AffineMatrix::operator=(angles); return *this; }
	LinearMatrix& operator=(const Quaternion &q)		{ AffineMatrix::operator=(q); return *this; }
	LinearMatrix& operator=(const Point3D &p)			{ AffineMatrix::operator=(p); return *this; }
	LinearMatrix& operator=(const AffineMatrix &m);
	LinearMatrix& operator=(const Matrix4x4 &m);
	LinearMatrix& operator=(const TransposedMatrix &m);

	void GetFromAxis(size_t index, UnitVector *v) const	{ AffineMatrix::GetFromAxis(index, v); }
	void GetToAxis(size_t index, UnitVector *v) const	{ AffineMatrix::GetToAxis(index, v); }

	LinearMatrix& SetFromAxis(size_t index, const UnitVector &v)
	{
		AffineMatrix::SetFromAxis(index, v);
		return *this;
	}
	LinearMatrix& SetToAxis(size_t index, const UnitVector &v)
	{
		AffineMatrix::SetToAxis(index, v);
		return *this;
	}

	LinearMatrix& Multiply(const LinearMatrix& m1, const LinearMatrix& m2)
	{
		AffineMatrix::Multiply(m1, m2);
		return *this;
	}
	LinearMatrix& operator*=(const LinearMatrix& M)
	{
		LinearMatrix src(*this);
		return Multiply(src, M);
	}

	LinearMatrix& Invert(const LinearMatrix& Source);
	LinearMatrix& Invert()
	{
		LinearMatrix src(*this);
		return Invert(src);
	}

	LinearMatrix& Multiply(const LinearMatrix &m, const Quaternion &q)
	{
		Check_Pointer(this);
		Check(&m);
		Check(&q);
		AffineMatrix::Multiply(m, q);
		return *this;
	}
	LinearMatrix& operator*=(const Quaternion &q)
	{
		Check(this);
		LinearMatrix m(*this);
		return Multiply(m, q);
	}
	LinearMatrix& Multiply(const LinearMatrix &m, const Point3D &p)
	{
		Check_Pointer(this);
		Check(&m);
		Check(&p);
		AffineMatrix::Multiply(m, p);
		return *this;
	}
	LinearMatrix& operator*=(const Point3D& p)
	{
		Check(this);
		LinearMatrix m(*this);
		return Multiply(m, p);
	}

	LinearMatrix& Normalize();

	Logical TestInstance() const;
	static Logical TestClass();

private:
	LinearMatrix& Solve();
};

inline UnitVector& UnitVector::Multiply(const UnitVector &v, const LinearMatrix &m)
{
	Check(&v);
	Vector3D::Multiply(v, m);
	return *this;
}

inline UnitVector& UnitVector::operator *=(const LinearMatrix &m)
{
	UnitVector src(*this);
	return Multiply(src, m);
}

inline Quaternion& Quaternion::Multiply(const Quaternion &q, const LinearMatrix &m)
{
	Cast_Object(Vector3D*, this)->Multiply(SKIPPY_CAST(Vector3D, q), m);
	return *this;
}

inline Quaternion& Quaternion::operator *=(const LinearMatrix &m)
{
	Quaternion t(*this);
	return Multiply(t, m);
}