
// zlib open source license
//
// Copyright (c) 2010 to 2013 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

int CDFPGECtrl::Instance_Create(int Model) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Model,Model)
		if BAD_REF(Model) {
			REPORT_TYPE(Model,L"Instance_Create",Model)
			Result = 0;
		} else {
			Result = DGE.IDFromPointer(DGE.Instance_Create(pModel));
		}
	)
	return Result;
}

void CDFPGECtrl::Instance_Delete(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_Delete",Instance)
		} else {
			DGE.Delete_Instance(pInstance);
		}
	)
}

void CDFPGECtrl::Instance_ReplaceModel(int Instance,int Model) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Model,Model)
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Model) {
			REPORT_TYPE(Model,L"Instance_ReplaceModel",Model)
		} else if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_ReplaceModel",Instance)
		} else {
			DGE.Instance_ReplaceModel(pInstance,pModel);
		}
	)
}

void CDFPGECtrl::Instance_AssignBoneFrame(int Instance,int BoneFrame) {
	START_OF_CRITICAL_SECTION
		GET_FROM_REF_ALLOW_ZERO(BoneFrame,BoneFrame)
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_AssignBoneFrame",Instance)
		} else if (BoneFrame == 0) {
			DGE.Instance_RemoveBoneFrame(pInstance);
		} else if BAD_REF(BoneFrame) {
			REPORT_TYPE(BoneFrame,L"Instance_AssignBoneFrame",BoneFrame)
		} else {
			DGE.Instance_AssignBoneFrame(pInstance,pBoneFrame);
		}
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::Instance_SetUserDefinedData(int Instance, int Index, float X,float Y,float Z,float W) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetUserDefinedData",Instance)
		} else if (Index < 0 || Index > NumberOfUserVectorsPerInstance - 1) {
			swprintf_s( MQ->messageBuffer, L"Instance_SetUserDefinedData: Index %i was out of bound [0..%i].",Index,NumberOfUserVectorsPerInstance - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else {
			pInstance->UserProperties_Instance[Index] = DVector4(X,Y,Z,W);
			pInstance->ModifiedArguments = true;
		}
	)
}

void CDFPGECtrl::Instance_GetUserDefinedData_OutV4(int Instance, int Index) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetUserDefinedData_OutV4",Instance)
		} else if (Index < 0 || Index > NumberOfUserVectorsPerInstance - 1) {
			swprintf_s( MQ->messageBuffer, L"Instance_GetUserDefinedData: Index %i was out of bound [0..%i].",Index,NumberOfUserVectorsPerInstance - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else {
			SetVector4ToMB(pInstance->UserProperties_Instance[Index]);
		}
	)
}

void CDFPGECtrl::Instance_SetPosition(int Instance, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetPosition",Instance)
		} else {
			pInstance->Transformation._41 = X;
			pInstance->Transformation._42 = Y;
			pInstance->Transformation._43 = Z;
		}
	)
}

void CDFPGECtrl::Instance_GetPosition_OutV3(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		ClearMatrixBuffer();
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetPosition_OutV3",Instance)
		} else {
			X1 = pInstance->Transformation._41;
			Y1 = pInstance->Transformation._42;
			Z1 = pInstance->Transformation._43;
		}
	)
}

void CDFPGECtrl::Instance_SetXAxis(int Instance, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetXAxis",Instance)
		} else {
			pInstance->Transformation._11 = X;
			pInstance->Transformation._12 = Y;
			pInstance->Transformation._13 = Z;
		}
	)
}

void CDFPGECtrl::Instance_GetXAxis_OutV3(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		ClearMatrixBuffer();
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetXAxis_OutV3",Instance)
		} else {
			X1 = pInstance->Transformation._11;
			Y1 = pInstance->Transformation._12;
			Z1 = pInstance->Transformation._13;
		}
	)
}

void CDFPGECtrl::Instance_SetYAxis(int Instance, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetYAxis",Instance)
		} else {
			pInstance->Transformation._21 = X;
			pInstance->Transformation._22 = Y;
			pInstance->Transformation._23 = Z;
		}
	)
}

void CDFPGECtrl::Instance_GetYAxis_OutV3(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		ClearMatrixBuffer();
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetYAxis_OutV3",Instance)
		} else {
			X1 = pInstance->Transformation._21;
			Y1 = pInstance->Transformation._22;
			Z1 = pInstance->Transformation._23;
		}
	)
}

void CDFPGECtrl::Instance_SetZAxis(int Instance, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetZAxis",Instance)
		} else {
			pInstance->Transformation._31 = X;
			pInstance->Transformation._32 = Y;
			pInstance->Transformation._33 = Z;
		}
	)
}

void CDFPGECtrl::Instance_GetZAxis_OutV3(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		ClearMatrixBuffer();
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetZAxis",Instance)
		} else {
			X1 = pInstance->Transformation._31;
			Y1 = pInstance->Transformation._32;
			Z1 = pInstance->Transformation._33;
		}
	)
}

void CDFPGECtrl::Instance_SetVisibility(int Instance, int ShaderChannel, bool Visible) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetVisibility",Instance)
		} else if (ShaderChannel < 0 || ShaderChannel >= NumberOfShaderChannels) {
			swprintf_s( MQ->messageBuffer, L"Instance_SetVisibility: ShaderChannel %i was out of bound [0..%i].",ShaderChannel,NumberOfShaderChannels-1); MQ->InsertMessage(MQ->messageBuffer);
		} else {
			pInstance->Visible[ShaderChannel] = Visible;
		}
	)
}

int CDFPGECtrl::Instance_GetVisibility(int Instance, int ShaderChannel) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetVisibility",Instance)
			Result = 0;
		} else if (ShaderChannel < 0 || ShaderChannel >= NumberOfShaderChannels) {
			swprintf_s( MQ->messageBuffer, L"Instance_GetVisibility: ShaderChannel %i was out of bound [0..%i].",ShaderChannel,NumberOfShaderChannels-1); MQ->InsertMessage(MQ->messageBuffer);
		} else if (pInstance->Visible[ShaderChannel]) {
			Result = 1;
		} else {
			Result = 0;
		}
	)
	return Result;
}

void CDFPGECtrl::Instance_SetMirrored(int Instance, bool Mirrored) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetMirrored",Instance)
		} else {
			pInstance->Mirrored = Mirrored;
		}
	)
}

int CDFPGECtrl::Instance_GetMirrored(int Instance) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetMirrored",Instance)
			Result = 0;
		} else if (pInstance->Mirrored) {
			Result = 1;
		} else {
			Result = 0;
		}
	)
	return Result;
}

void CDFPGECtrl::Instance_SetColor(int Instance, float Red, float Green, float Blue, float Alpha) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetColor",Instance)
		} else {
			pInstance->Color = DVector4(Red,Green,Blue,Alpha);
		}
	)
}

void CDFPGECtrl::Instance_GetColor_OutV4(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		ClearMatrixBuffer();
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetColor_OutV4",Instance)
		} else {
			X1 = pInstance->Color.x;
			Y1 = pInstance->Color.y;
			Z1 = pInstance->Color.z;
			W1 = pInstance->Color.w;
		}
	)
}

void CDFPGECtrl::Instance_Get4x4System_OutM4(int Instance) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_Get4x4System_OutM4",Instance)
		} else {
			X1 = pInstance->Transformation._11;
			Y1 = pInstance->Transformation._12;
			Z1 = pInstance->Transformation._13;
			W1 = 0;
			X2 = pInstance->Transformation._21;
			Y2 = pInstance->Transformation._22;
			Z2 = pInstance->Transformation._23;
			W2 = 0;
			X3 = pInstance->Transformation._31;
			Y3 = pInstance->Transformation._32;
			Z3 = pInstance->Transformation._33;
			W3 = 0;
			X4 = pInstance->Transformation._41;
			Y4 = pInstance->Transformation._42;
			Z4 = pInstance->Transformation._43;
			W4 = 1;
		}
	)
}

void CDFPGECtrl::Instance_SetTextureOverride(int Instance, int OverrideChannel, int DrawSurfaceOrTexture) {
	CRITICAL_CODE_SECTION(
		void* pDrawSurfaceOrTexture;
		int ReferenceType;
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetTextureOverride",Instance)
		} else if (OverrideChannel < 0 || OverrideChannel > 15) {
			swprintf_s( MQ->messageBuffer, L"Instance_SetTextureOverride: OverrideChannel equals %i and outside of the valid bound [0..%i]",OverrideChannel,NumberOfOverrideChannels-1); MQ->InsertMessage(MQ->messageBuffer);
		} else if (DrawSurfaceOrTexture == 0) {
			DGE.Instance_ReleaseOverride(pInstance,OverrideChannel);
		} else {
			pDrawSurfaceOrTexture = DGE.VoidPointerFromID(DrawSurfaceOrTexture);
			ReferenceType = DGE.TypeFromID(DrawSurfaceOrTexture);
			if (ReferenceType == ColType_Texture) {
				DGE.Instance_SetOverrideUsingTexture(pInstance,OverrideChannel,(Texture_Struct*)pDrawSurfaceOrTexture);
			} else if (ReferenceType == ColType_DrawSurface) {
				DGE.Instance_SetOverrideUsingDrawSurface(pInstance,OverrideChannel,(DrawSurface_Struct*)pDrawSurfaceOrTexture);
			} else {
				swprintf_s( MQ->messageBuffer, L"Instance_SetTextureOverride: DrawSurfaceOrTexture equals %i and is not 0 or a valid draw surface or texture.",DrawSurfaceOrTexture); MQ->InsertMessage(MQ->messageBuffer);
			}
		}
	)
}

int CDFPGECtrl::Instance_GetTextureOverride(int Instance, int OverrideChannel) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetTextureOverride",Instance)
			return -1;
		} else if (OverrideChannel < 0 || OverrideChannel > 15) {
			swprintf_s( MQ->messageBuffer, L"Instance_GetTextureOverride: OverrideChannel equals %i and outside of the valid bound [0..%i]",OverrideChannel,NumberOfOverrideChannels-1); MQ->InsertMessage(MQ->messageBuffer);
			return -1;
		} else {
			return DGE.Instance_GetIDFromOverride(pInstance,OverrideChannel);
		}
	)
}

void CDFPGECtrl::Instance_SetDetailLevel(int Instance, int DetailLevel) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetDetailLevel",Instance)
		} else {
			pInstance->DetailLevel_Int = ClampInt(0,DetailLevel,2);
		}
	)
}

void CDFPGECtrl::Instance_SetDetailLevel_Float(int Instance, float DetailLevel) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetDetailLevel_Float",Instance)
		} else {
			pInstance->DetailLevel_Float = ClampFloat(0.0f,DetailLevel,2.0f);
		}
	)
}

int CDFPGECtrl::Instance_GetDetailLevel(int Instance) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetDetailLevel",Instance)
			Result = -1;
		} else {
			Result = pInstance->DetailLevel_Int;
		}
	)
	return Result;
}

float CDFPGECtrl::Instance_GetDetailLevel_Float(int Instance) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetDetailLevel_Float",Instance)
			Result = -1.0f;
		} else {
			Result = pInstance->DetailLevel_Float;
		}
	)
	return Result;
}

void CDFPGECtrl::Instance_SetAutoDetailLevel(int Instance, bool AutoDetailLevel) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_SetAutoDetailLevel",Instance)
		} else {
			pInstance->AutoDetailLevel = AutoDetailLevel;
		}
	)
}

int CDFPGECtrl::Instance_GetAutoDetailLevel(int Instance) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetAutoDetailLevel",Instance)
			Result = 0;
		} else if (pInstance->AutoDetailLevel) {
			Result = 1;
		} else {
			Result = 0;
		}
	)
	return Result;
}

void CDFPGECtrl::Instance_PlaceAtBulletTransform(Instance_Struct* pInstance, btTransform* NewTrans) {
	START_OF_CRITICAL_SECTION
		float M[12];
		btVector3 T;
		NewTrans->getBasis().getOpenGLSubMatrix(M);
		T = NewTrans->getOrigin();
		pInstance->Transformation._11 = M[0];
		pInstance->Transformation._12 = M[1];
		pInstance->Transformation._13 = M[2];
		pInstance->Transformation._21 = M[4];
		pInstance->Transformation._22 = M[5];
		pInstance->Transformation._23 = M[6];
		pInstance->Transformation._31 = M[8];
		pInstance->Transformation._32 = M[9];
		pInstance->Transformation._33 = M[10];
		pInstance->Transformation._41 = T.getX();
		pInstance->Transformation._42 = T.getY();
		pInstance->Transformation._43 = T.getZ();
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::Instance_PlaceAtBulletTransform_Scaled(Instance_Struct* pInstance, btTransform* NewTrans, btVector3* LocalScaling) {
	START_OF_CRITICAL_SECTION
		float M[12];
		btVector3 T;
		NewTrans->getBasis().getOpenGLSubMatrix(M);
		T = NewTrans->getOrigin();
		pInstance->Transformation._11 = M[0] * LocalScaling->getX();
		pInstance->Transformation._12 = M[1] * LocalScaling->getX();
		pInstance->Transformation._13 = M[2] * LocalScaling->getX();
		pInstance->Transformation._21 = M[4] * LocalScaling->getY();
		pInstance->Transformation._22 = M[5] * LocalScaling->getY();
		pInstance->Transformation._23 = M[6] * LocalScaling->getY();
		pInstance->Transformation._31 = M[8] * LocalScaling->getZ();
		pInstance->Transformation._32 = M[9] * LocalScaling->getZ();
		pInstance->Transformation._33 = M[10] * LocalScaling->getZ();
		pInstance->Transformation._41 = T.getX();
		pInstance->Transformation._42 = T.getY();
		pInstance->Transformation._43 = T.getZ();
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::Instance_PlaceAtRigidBody(int Instance, int RigidBody, bool UseLocalScaling) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		GET_FROM_REF(RigidBody,RigidBody)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_PlaceAtRigidBody",Instance)
		} else if BAD_REF(RigidBody) {
			REPORT_TYPE(RigidBody,L"Instance_PlaceAtRigidBody",RigidBody)
		} else {
			PlaceAtRigidBody(pInstance,pRigidBody,UseLocalScaling);
		}
	)
}

void CDFPGECtrl::Instance_PlaceAtRigidBody_Interpolated(int Instance, int RigidBody, float TimeOffset, bool UseLocalScaling) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		GET_FROM_REF(RigidBody,RigidBody)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_PlaceAtRigidBody_Interpolated",Instance)
		} else if BAD_REF(RigidBody) {
			REPORT_TYPE(RigidBody,L"Instance_PlaceAtRigidBody_Interpolated",RigidBody)
		} else {
			PlaceAtRigidBody_Interpolated(pInstance,pRigidBody,TimeOffset,UseLocalScaling);
		}
	)
}

void CDFPGECtrl::Instance_FollowRigidBody(int Instance, int RigidBody, bool UseLocalScaling) {
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_FollowRigidBody",Instance)
		} else {
			if (pInstance->FollowedRigidBody) {
				StopUsing(pInstance->FollowedRigidBody->useCount);
				pInstance->FollowedRigidBody->LL_Follow.Remove(&(pInstance->LL_Follow));
			}
			if (RigidBody) {
				GET_FROM_REF(RigidBody,RigidBody)
				if BAD_REF(RigidBody) {
					REPORT_TYPE(RigidBody,L"Instance_FollowRigidBody",RigidBody)
				} else {
					pInstance->FollowedRigidBody = pRigidBody;
					StartUsing(pRigidBody->useCount);
					pInstance->UseLocalScaling = UseLocalScaling;
					pRigidBody->LL_Follow.Insert(&(pInstance->LL_Follow),pInstance);
					PlaceAtRigidBody(pInstance,pRigidBody,UseLocalScaling);
				}
			} else {
				pInstance->FollowedRigidBody = NULL;
				pInstance->UseLocalScaling = false;
			}
		}
	END_OF_CRITICAL_SECTION
}

int CDFPGECtrl::Instance_GetFollowedRigidBody(int Instance) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Instance,Instance)
		if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Instance_GetFollowedRigidBody",Instance)
			Result = -1;
		} else {
			Result = DGE.IDFromPointer(pInstance->FollowedRigidBody);
		}
	)
	return Result;
}
