
// 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::Camera_Create(void) {
	int Result;
	CRITICAL_CODE_SECTION(
		if (DGE.m_Running) {
			Result = DGE.IDFromPointer(DGE.Camera_Create());
		} else {
			REPORT_NOT_RUNNING(L"Camera_Create")
			Result = 0;
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_Delete(int Camera) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_Delete",Camera)
		} else {
			DGE.Delete_Camera(pCamera);
		}
	)
}

void CDFPGECtrl::Camera_RenderScene(int InputCamera, int OutputSurface, int ShaderChannel) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,InputCamera)
		GET_FROM_REF(DrawSurface,OutputSurface)
		if BAD_REF(InputCamera) {
			REPORT_TYPE(Camera,L"Camera_RenderScene",InputCamera)
		} else if BAD_REF(OutputSurface) {
			REPORT_TYPE(DrawSurface,L"Camera_RenderScene",OutputSurface)
		} else if (pOutputSurface->DepthBuffer == NULL) {
			MQ->InsertMessage(L"Camera_RenderScene: The draw surface reference OutputSurface does not have a depth buffer.");
		} else if (pOutputSurface->MaterialCount > 0 && pOutputSurface->HaveExtraColorBuffer == false) {
			MQ->InsertMessage(L"Camera_RenderScene: Rendering the world to a draw surface that is used in a material require the draw surface to be created with an extra color buffer.");
		} else if (ShaderChannel < 0 || ShaderChannel >= NumberOfShaderChannels) {
			swprintf_s( MQ->messageBuffer, L"Camera_RenderScene: ShaderChannel %i was out of bound [0..%i].",ShaderChannel,NumberOfShaderChannels - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else {
			DGE.Camera_Render(pInputCamera,pOutputSurface,ShaderChannel,NULL,NULL,0,0,true,DVector4(0.0f,0.0f,0.0f,0.0f),-1);
		}
	)
}

void CDFPGECtrl::Camera_RenderInstance(int InputCamera, int OutputSurface, int Instance, int MaterialShaderOverride, int FilterIndex, int RenderingMethod, bool UseDepthBuffer, float InstanceColor_Red, float InstanceColor_Green, float InstanceColor_Blue, float InstanceColor_Alpha, int Part) {
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(Camera,InputCamera)
		GET_FROM_REF(DrawSurface,OutputSurface)
		GET_FROM_REF(Instance,Instance)
		GET_FROM_REF_ALLOW_ZERO(Shader,MaterialShaderOverride)
		if BAD_REF(InputCamera) {
			REPORT_TYPE(Camera,L"Camera_RenderInstance",InputCamera)
		} else if BAD_REF(OutputSurface) {
			REPORT_TYPE(DrawSurface,L"Camera_RenderInstance",OutputSurface)
		} else if BAD_REF(Instance) {
			REPORT_TYPE(Instance,L"Camera_RenderInstance",Instance)
		} else if (pOutputSurface->DepthBuffer == NULL && UseDepthBuffer) {
			MQ->InsertMessage(L"Camera_RenderInstance: The draw surface reference OutputSurface does not have a depth buffer.");
		} else if (pOutputSurface->MaterialCount > 0) {
			MQ->InsertMessage(L"Camera_RenderInstance: Rendering an instance to a draw surface that is used in a material is not allowed.");
		} else if (FilterIndex < 0 || FilterIndex > NumberOfFilterTypes - 1) {
			swprintf_s( MQ->messageBuffer, L"Camera_RenderInstance: FilterIndex %i is not a valid filter index [0..%i].",FilterIndex,NumberOfFilterTypes - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else if (RenderingMethod < 0 || RenderingMethod > NumberOfRenderingMethods - 1) {
			swprintf_s( MQ->messageBuffer, L"Camera_RenderInstance: RenderingMethod %i is not a valid rendering method [0..%i].",RenderingMethod,NumberOfRenderingMethods - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else if (Part < -1 || Part > pInstance->VisualModel->Model.NumberOfParts - 1) {
			swprintf_s( MQ->messageBuffer, L"Camera_RenderInstance: Part %i is not -1 for all parts or a valid part index [0..%i].",Part,pInstance->VisualModel->Model.NumberOfParts - 1); MQ->InsertMessage(MQ->messageBuffer);
		} else if (MaterialShaderOverride == 0) {
			if (pInstance->VisualModel->Model.IsPartVisible(pInstance->DetailLevel_Int,Part)) {
				DGE.Camera_Render(pInputCamera,pOutputSurface,0,pInstance,NULL,FilterIndex,RenderingMethod,UseDepthBuffer,DVector4(InstanceColor_Red,InstanceColor_Green,InstanceColor_Blue,InstanceColor_Alpha),Part);
			}
		} else if (BAD_REF(MaterialShaderOverride)) {
			MQ->InsertMessage(L"Camera_RenderInstance: MaterialShaderOverride is not 0 or a valid shader reference.");
		} else if (pMaterialShaderOverride->ShaderType != ShaderType_Material) {
			MQ->InsertMessage(L"Camera_RenderInstance: MaterialShaderOverride is a valid shader but not a material shader.");
		} else {
			if (pInstance->VisualModel->Model.IsPartVisible(pInstance->DetailLevel_Int,Part)) {
				DGE.Camera_Render(pInputCamera,pOutputSurface,0,pInstance,pMaterialShaderOverride,FilterIndex,RenderingMethod,UseDepthBuffer,DVector4(InstanceColor_Red,InstanceColor_Green,InstanceColor_Blue,InstanceColor_Alpha),Part);
			}
		}
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::Camera_Place(int Camera, float Position_X, float Position_Y, float Position_Z, float Target_X, float Target_Y, float Target_Z, float Up_X, float Up_Y, float Up_Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_Place",Camera)
		} else {
			DGE.Camera_Place(pCamera,DVector3( Position_X, Position_Y, Position_Z ), DVector3( Target_X, Target_Y, Target_Z ), DVector3( Up_X, Up_Y, Up_Z ));
		}
	)
}

void CDFPGECtrl::Camera_GetPosition_OutV3(int Camera) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetPosition_OutV3",Camera)
		} else {
			SetVector3ToMB(pCamera->Eye);
		}
	)
}

void CDFPGECtrl::Camera_GetTarget_OutV3(int Camera) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetTarget_OutV3",Camera)
		} else {
			SetVector3ToMB(pCamera->At);
		}
	)
}

void CDFPGECtrl::Camera_GetUp_OutV3(int Camera) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetUp_OutV3",Camera)
		} else {
			SetVector3ToMB(pCamera->Up);
		}
	)
}

void CDFPGECtrl::Camera_SetHorizontalFOV_InDegrees(int Camera, float FOV) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetHorizontalFOV_InDegrees",Camera)
		} else {
			DGE.Camera_SetHorizontalFOV_InDegrees(pCamera, FOV);
		}
	)
}

void CDFPGECtrl::Camera_SetVerticalFOV_InDegrees(int Camera, float FOV) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetVerticalFOV_InDegrees",Camera)
		} else {
			DGE.Camera_SetVerticalFOV_InDegrees(pCamera, FOV);
		}
	)
}

void CDFPGECtrl::Camera_SetHorizontalFOV_InRadians(int Camera, float FOV) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetHorizontalFOV_InRadians",Camera)
		} else {
			DGE.Camera_SetHorizontalFOV_InRadians(pCamera, FOV);
		}
	)
}

void CDFPGECtrl::Camera_SetVerticalFOV_InRadians(int Camera, float FOV) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetVerticalFOV_InRadians",Camera)
		} else {
			DGE.Camera_SetVerticalFOV_InRadians(pCamera, FOV);
		}
	)
}

float CDFPGECtrl::Camera_GetFieldOfViewInRadians(int Camera) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetFieldOfViewInRadians",Camera)
			Result = 0.0f;
		} else {
			Result = pCamera->FieldOfView;
		}
	)
	return Result;
}

float CDFPGECtrl::Camera_GetFieldOfViewInDegrees(int Camera) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetFieldOfViewInDegrees",Camera)
			Result = 0.0f;
		} else {
			Result = pCamera->FieldOfView * 57.295779513082320876798154814105f;
		}
	)
	return Result;
}

int CDFPGECtrl::Camera_IsFieldOfViewVertical(int Camera) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_IsFieldOfViewVertical",Camera)
			Result = 0;
		} else {
			if (pCamera->VerticalFieldOfView) {
				Result = 1;
			} else {
				Result = 0;
			}
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_Get4x4System_OutM4(int Camera) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_Get4x4System_OutM4",Camera)
		} else {
			DMatrix CameraMatrix;
			CameraMatrix = DGE.Camera_GetAxisSystem(pCamera);
			X1 = CameraMatrix._11;
			Y1 = CameraMatrix._12;
			Z1 = CameraMatrix._13;
			W1 = CameraMatrix._14;
			X2 = CameraMatrix._21;
			Y2 = CameraMatrix._22;
			Z2 = CameraMatrix._23;
			W2 = CameraMatrix._24;
			X3 = CameraMatrix._31;
			Y3 = CameraMatrix._32;
			Z3 = CameraMatrix._33;
			W3 = CameraMatrix._34;
			X4 = CameraMatrix._41;
			Y4 = CameraMatrix._42;
			Z4 = CameraMatrix._43;
			W4 = CameraMatrix._44;
		}
	)
}

// Precondition: |(NX,NY,NZ)| = 1 (The plane's direction will be normalized but the equation is not true for incorrect values)
// Side effect: Clip a pixel if (X * NX) + (Y * NY) + (Z * NZ) < D in world space
void CDFPGECtrl::Camera_GiveCuttingPlane(int Camera, float NX, float NY, float NZ, float D, float RefractiveMultiplier) {
	CRITICAL_CODE_SECTION(
		DVector3 Normal;
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GiveCuttingPlane",Camera)
		} else if ( RefractiveMultiplier <= 0) {
			MQ->InsertMessage(L"Camera_GiveCuttingPlane: RefractiveMultiplier is not above 0. Render the world from another place and flip the result if you need a reflection.");
		} else {
			pCamera->EnableCuttingPlane = true;
			Normal = NormalizeVec3(DVector3(NX,NY,NZ));
			pCamera->CuttingPlane = DVector4(Normal.x,Normal.y,Normal.z,D);
			pCamera->RefractiveMultiplier = RefractiveMultiplier;
		}
	)
}

//Remove the cutting plane
void CDFPGECtrl::Camera_RemoveCuttingPlane(int Camera) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_RemoveCuttingPlane",Camera)
		} else {
			pCamera->EnableCuttingPlane = false;
		}
	)
}

int CDFPGECtrl::Camera_HasCuttingPlane(int Camera) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_HasCuttingPlane",Camera)
			Result = 0;
		} else if (pCamera->EnableCuttingPlane) {
			Result = 1;
		} else {
			Result = 0;
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_GetCuttingPlane_OutV4(int Camera) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetCuttingPlane_OutV4",Camera)
		} else {
			SetVector4ToMB(pCamera->CuttingPlane);
		}
	)
}

float CDFPGECtrl::Camera_GetRefractiveMultiplier(int Camera) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetRefractiveMultiplier",Camera)
			Result = 1.0f;
		} else {
			Result = pCamera->RefractiveMultiplier;
		}
	)
	return Result;
}

int CDFPGECtrl::Camera_IsOrthogonal(int Camera) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_IsOrthogonal",Camera)
			Result = 0;
		} else {
			if (pCamera->IsOrthogonal) {
				Result = 1;
			} else {
				Result = 0;
			}
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_SetOrthogonal(int Camera, bool Orthogonal) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetOrthogonal",Camera)
		} else {
			pCamera->IsOrthogonal = Orthogonal;
		}
	)
}

float CDFPGECtrl::Camera_GetHalfWidth(int Camera) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetHalfWidth",Camera)
			Result = 0.0f;
		} else {
			Result = pCamera->OrthogonalHalfWidth;
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_SetHalfWidth(int Camera, float HalfWidth) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetHalfWidth",Camera)
		} else {
			pCamera->OrthogonalHalfWidth = HalfWidth;
		}
	)
}

float CDFPGECtrl::Camera_GetHalfHeight(int Camera) {
	float Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetHalfHeight",Camera)
			Result = 0.0f;
		} else {
			Result = pCamera->OrthogonalHalfHeight;
		}
	)
	return Result;
}

void CDFPGECtrl::Camera_SetHalfHeight(int Camera, float HalfHeight) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetHalfHeight",Camera)
		} else {
			pCamera->OrthogonalHalfHeight = HalfHeight;
		}
	)
}


void CDFPGECtrl::Camera_GetDirection_OutV3(int Camera) {
	CRITICAL_CODE_SECTION(
		DVector3 Direction;
		ClearMatrixBuffer();
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetDirection_OutV3",Camera)
		} else {
			Direction = SubVec3(pCamera->At,pCamera->Eye);
			Direction = NormalizeVec3(Direction);
			SetVector3ToMB(Direction);
		}
	)
}

void CDFPGECtrl::Camera_SetDebugViewVisibility(int Camera, int DebugViewIndex, bool Visible) {
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_SetDebugViewVisibility",Camera)
		} else if (DebugViewIndex < 0 || DebugViewIndex >= NumberOfDebugDrawFlags) {
			MQ->InsertMessage(L"Camera_SetDebugViewVisibility: DebugViewIndex is out of bound.");
		} else {
			SetBitInUINT16(&(pCamera->DebugDraw), 15 - DebugViewIndex, Visible);
		}
	END_OF_CRITICAL_SECTION
}

int CDFPGECtrl::Camera_GetDebugViewVisibility(int Camera, int DebugViewIndex) {
	int Result;
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(Camera,Camera)
		if BAD_REF(Camera) {
			REPORT_TYPE(Camera,L"Camera_GetDebugViewVisibility",Camera)
			Result = 0;
		} else if (DebugViewIndex < 0 || DebugViewIndex >= NumberOfDebugDrawFlags) {
			MQ->InsertMessage(L"Camera_GetDebugViewVisibility: DebugViewIndex is out of bound.");
			Result = 0;
		} else {
			if (GetBitFromUINT16(pCamera->DebugDraw, 15 - DebugViewIndex)) {
				Result = 1;
			} else {
				Result = 0;
			}
		}
	END_OF_CRITICAL_SECTION
	return Result;
}
