//========================================================================
// E3D library
//
// Matrix operations
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Nov-17 02:48:37
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//========================================================================
#ifndef _E3D_Matrix_h
#define _E3D_Matrix_h


#ifndef _E3D_Types_h
#include <E3D/Types.h>
#endif

#include <EMatrix.h>

//----------------------------------------------------------------
// Macro functions
//----------------------------------------------------------------

//
// Suffixes for input and output arguments
//
// I : implicit (pX, pY, pZ declared and set by the caller)
// V : values   (InX, InY, InZ)
// S : struct  (In.X, In.Y, In.Z)
// P : struct pointer  (In->X, In->Y, In->Z)
//
// If there is only one suffix, the input and output will be the same variable
//

#ifdef E3dSIMD
#define _M_ClearWs(p)	p.unused = 0.0
#define _M_ClearWp(p)	p->unused = 0.0
#else
#define _M_ClearWs(p)
#define _M_ClearWp(p)
#endif


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

typedef struct
{
	int         Pointer, AllocatedDepth;
	E3dMatrix*  Matrices;
} E3dMatrixStack;


//================================================
// Rotate Matrix around the X axis - radians
//================================================
#define E3d_MatrixRotateX_rad(pMatrix, pAngle) do\
{\
	const E3dCoordinate  lCosA = E3d_Cos(-pAngle), lSinA = E3d_Sin(-pAngle);\
	const E3dCoordinate  lMSinA = -lSinA;\
	E3dMatrix            lTMatrix;\
\
	lTMatrix[M10] = pMatrix[M10] * lCosA + pMatrix[M20] * lSinA;lTMatrix[M11] = pMatrix[M11] * lCosA + pMatrix[M21] * lSinA;lTMatrix[M12] = pMatrix[M12] * lCosA + pMatrix[M22] * lSinA;\
	lTMatrix[M20] = pMatrix[M10] * lMSinA + pMatrix[M20] * lCosA;lTMatrix[M21] = pMatrix[M11] * lMSinA + pMatrix[M21] * lCosA;lTMatrix[M22] = pMatrix[M12] * lMSinA + pMatrix[M22] * lCosA;\
\
	pMatrix[M10] = lTMatrix[M10];pMatrix[M11] = lTMatrix[M11];pMatrix[M12] = lTMatrix[M12];\
	pMatrix[M20] = lTMatrix[M20];pMatrix[M21] = lTMatrix[M21];pMatrix[M22] = lTMatrix[M22];\
} while(0)


//================================================
// Rotate Matrix around the X axis - radians
//================================================
#define E3d_MatrixRotateY_rad(pMatrix, pAngle) do\
{\
	const E3dCoordinate  lCosA = E3d_Cos(-pAngle), lSinA = E3d_Sin(-pAngle);\
	const E3dCoordinate  lMSinA = -lSinA;\
	E3dMatrix            lTMatrix;\
\
	lTMatrix[M00] = pMatrix[M00] * lCosA + pMatrix[M20] * lMSinA;lTMatrix[M01] = pMatrix[M01] * lCosA + pMatrix[M21] * lMSinA;lTMatrix[M02] = pMatrix[M02] * lCosA + pMatrix[M22] * lMSinA;\
	lTMatrix[M20] = pMatrix[M00] * lSinA + pMatrix[M20] * lCosA; lTMatrix[M21] = pMatrix[M01] * lSinA + pMatrix[M21] * lCosA; lTMatrix[M22] = pMatrix[M02] * lSinA + pMatrix[M22] * lCosA;\
\
	pMatrix[M00] = lTMatrix[M00];pMatrix[M01] = lTMatrix[M01];pMatrix[M02] = lTMatrix[M02];\
	pMatrix[M20] = lTMatrix[M20];pMatrix[M21] = lTMatrix[M21];pMatrix[M22] = lTMatrix[M22];\
} while(0)


//================================================
// Rotate Matrix around the X axis - radians
//================================================
#define E3d_MatrixRotateZ_rad(pMatrix, pAngle) do\
{\
	const E3dCoordinate  lCosA = E3d_Cos(-pAngle), lSinA = E3d_Sin(-pAngle);\
	const E3dCoordinate  lMSinA = -lSinA;\
	E3dMatrix            lTMatrix;\
\
	lTMatrix[M00] = pMatrix[M00] * lCosA + pMatrix[M10] * lSinA;lTMatrix[M01] = pMatrix[M01] * lCosA + pMatrix[M11] * lSinA;lTMatrix[M02] = pMatrix[M02] * lCosA + pMatrix[M12] * lSinA;\
	lTMatrix[M10] = pMatrix[M00] * lMSinA + pMatrix[M10] * lCosA;lTMatrix[M11] = pMatrix[M01] * lMSinA + pMatrix[M11] * lCosA;lTMatrix[M12] = pMatrix[M02] * lMSinA + pMatrix[M12] * lCosA;\
\
	pMatrix[M00] = lTMatrix[M00];pMatrix[M01] = lTMatrix[M01];pMatrix[M02] = lTMatrix[M02];\
	pMatrix[M10] = lTMatrix[M10];pMatrix[M11] = lTMatrix[M11];pMatrix[M12] = lTMatrix[M12];\
} while(0)



//================================================
// Matrix constructors
//================================================
static inline void E3dMatrix3x3_VectorsToRows3x3_ppp(E3dMatrix PMatrix, const E3dCoord3* PV0, const E3dCoord3* PV1, const E3dCoord3* PV2)
{
	PMatrix[M00] = PV0->X;PMatrix[M01] = PV0->Y;PMatrix[M02] = PV0->Z;
	PMatrix[M10] = PV1->X;PMatrix[M11] = PV1->Y;PMatrix[M12] = PV1->Z;
	PMatrix[M20] = PV2->X;PMatrix[M21] = PV2->Y;PMatrix[M22] = PV2->Z;
}


static inline void E3dMatrix3x3_VectorsToCols3x3_ppp(E3dMatrix PMatrix, const E3dCoord3* PV0, const E3dCoord3* PV1, const E3dCoord3* PV2)
{
	PMatrix[M00] = PV0->X;PMatrix[M10] = PV0->Y;PMatrix[M20] = PV0->Z;
	PMatrix[M01] = PV1->X;PMatrix[M11] = PV1->Y;PMatrix[M21] = PV1->Z;
	PMatrix[M02] = PV2->X;PMatrix[M12] = PV2->Y;PMatrix[M22] = PV2->Z;
}



//----------------------------------------------------------------
// Global variables
//----------------------------------------------------------------
extern E3dMatrix      E3d_IdentityMatrix;


//----------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------
extern void           E3dMatrix_Print3x3(const E3dMatrix PMatrix);
extern void           E3dMatrix_Print4x4(const E3dMatrix PMatrix);

extern void           E3dMatrix_LoadIdentity(E3dMatrix PDestMtx);
extern void           E3dMatrix_Copy(const E3dMatrix PSrcMtx, E3dMatrix PDestMtx);

extern E3dCoordinate  E3dMatrix_Determinant3x3(const E3dMatrix PSrc);
extern void           E3dMatrix_Transpose3x3(const E3dMatrix PM, E3dMatrix PDst);

extern void           E3dMatrix_TransposeAdjoint3x3(const E3dMatrix PM, E3dMatrix PDst);
extern void           E3dMatrix_Adjoint3x3(const E3dMatrix PSrc, E3dMatrix PDst);
extern void           E3dMatrix_Invert3x3(const E3dMatrix PM, E3dMatrix PDst);

extern void           E3dMatrix_Adjoint3x4(const E3dMatrix PM, E3dMatrix PDst);
extern void           E3dMatrix_Invert3x4(const E3dMatrix PM, E3dMatrix PDst);
extern EBool          E3dMatrix_Invert4x4(const E3dMatrix PM, E3dMatrix PDst);

extern void           E3dMatrix_Transpose4x4(const E3dMatrix PM, E3dMatrix PDst);
extern EBool          E3d_Matrix4x4Invert(const E3dMatrix PSMatrix, E3dMatrix PDMatrix);

extern void           E3dMatrix_Copy3x3(const E3dMatrix PSrcMtx, E3dMatrix PDestMtx);
extern void           E3dMatrix_Copy3x4(const E3dMatrix PSrcMtx, E3dMatrix PDestMtx);

extern void           E3dMatrix_Transform3DDiv(const E3dMatrix PMatrix,  E3dCoord3* PPos);

extern void           E3dMatrix_Mult4x4(const E3dMatrix PSrcMtx, E3dMatrix PDestMtx);
extern void           E3dMatrix_Mult3x3_3(const E3dMatrix PMatrix1, const E3dMatrix PMatrix2, E3dMatrix PDestMtx);
extern void           E3dMatrix_Mult4x4_3(const E3dMatrix PMatrix1, const E3dMatrix PMatrix2, E3dMatrix PDestMtx);

extern EBool          E3dMatrix_Equal(const E3dMatrix PMtx1, const E3dMatrix PMtx2);
extern void           E3dMatrix_LoadTranslation(E3dMatrix PMatrix, const E3dCoordinate PX, const E3dCoordinate PY, const E3dCoordinate PZ);
extern void           E3dMatrix_Translate(E3dMatrix PMatrix, const E3dCoordinate PX, const E3dCoordinate PY, const E3dCoordinate PZ);
extern void           E3dMatrix_Scale(E3dMatrix PMatrix, const E3dCoordinate PX, const E3dCoordinate PY, const E3dCoordinate PZ);


extern void           E3dMatrix_Rotate2DRad(E3dMatrix PMatrix, const E3dCoordinate PAngle);
extern void           E3dMatrix_RotateDeg(E3dMatrix PMatrix, const int PAxis, const E3dCoordinate PAngle);
extern void           E3dMatrix_RotateRad(E3dMatrix PMatrix, const int PAxis, const E3dCoordinate PAngle);

extern void           E3dMatrix_RotateSinCos(E3dMatrix PMatrix, const int PAxis, const E3dCoordinate PSinA, const E3dCoordinate PCosA);
extern void           E3dMatrix_RotateXYZ(E3dMatrix PMatrix, const E3dCoord3* PAngles, const char POrder);
extern void           E3dMatrix_RotateAxisAngle(E3dMatrix PMatrix, const E3dCoord3* PAxis, const E3dCoordinate PAngle);

extern void           E3dMatrix_ToEuler(const E3dMatrix PMatrix, const char PRotOrder, E3dCoord3* PAngles);

extern void           E3dMatrix_SetLookAtInv(E3dMatrix PMatrix, const E3dCoord3* LPosition, const E3dCoord3* PAimPoint, const E3dCoord3* PUpVector);
extern void           E3dMatrix_LookAt(E3dMatrix PMatrix, const E3dCoord3* LPosition, const E3dCoord3* PAimPoint, const E3dCoord3* PUpVector);
extern void           E3dMatrix_SetLookAt(E3dMatrix PMatrix, const E3dCoord3* LPosition, const E3dCoord3* PAimPoint, const E3dCoord3* PUpVector);

extern void           E3dMatrix_SetOrtho(E3dMatrix PMatrix, const E3dCoordinate PLeft, const E3dCoordinate PRight, const E3dCoordinate PBottom, const E3dCoordinate PTop,  const E3dCoordinate PZNear, const E3dCoordinate PZFar);
extern void           E3dMatrix_SetFrustum(E3dMatrix PMatrix,  const E3dCoordinate PLeft, const E3dCoordinate PRight, const E3dCoordinate PBottom, const E3dCoordinate PTop,  const E3dCoordinate PZNear, const E3dCoordinate PZFar);
extern void           E3dMatrix_SetPerspective(E3dMatrix PMatrix, const E3dCoordinate PFieldOfView, const E3dCoordinate PAspectRatio, const E3dCoordinate PZNear, const E3dCoordinate PZFar);


extern void           E3dMatrix_ToQuaternion(const E3dMatrix PMatrix, E3dCoord4* PQuaternion);
extern void           E3dQuaternion_ToMatrix(const E3dCoord4* PQuaternion, E3dMatrix PMatrix);
extern void           E3d_AxisAngleToMatrix(const E3dCoord4* PAA, E3dMatrix PMatrix);

extern void           E3d_VectorsGetRotMatrix(const E3dCoord3* P1, const E3dCoord3* P2, E3dMatrix PMatrix);
extern void           E3d_NormalGetDefRotMatrix(const E3dCoord3* PNormal, E3dMatrix PMatrix);
extern void           E3d_TangentAndNormalToRotationMatrix(const E3dCoord3* PTangent, const E3dCoord3* PNormal, E3dMatrix PMatrix);

extern void           E3dMatrix3x3_FromVectors(E3dMatrix PMatrix,  const E3dCoord3 PXAxis, const E3dCoord3 PYAxis, const E3dCoord3 PZAxis);


extern void           E3dEulerRotation_Init(E3dEulerRotation* PEulerRotation);
extern void           E3dRotation_Init(E3dRotation* PRotation);
extern void           E3dRotation_Set(E3dRotation* PRotation, const E3dCoordinate PX, const E3dCoordinate PY, const E3dCoordinate PZ, const E3dCoordinate PW);

extern EBool          E3d_UnProject(const E3dCoordinate PWindowX, const E3dCoordinate PWindowY, const E3dCoordinate PWindowZ,
                                    const EIndex PViewport[4], E3dMatrix PProjectionMatrix, E3dMatrix PModelViewMatrix,
                                    E3dCoord3* PPositionP);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _E3D_Matrix_h
