
// 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.

#define FeedTextToStringBuffer(CODE) m_preCalculatedLength = 0; writeIndex = 0; counting = true; CODE SetLengthOfStringBuffer_Aux(m_preCalculatedLength); counting = false; CODE
#define FEEDSTRING(MESSAGE) FeedString(counting,MESSAGE);
#define FEEDYESORNO(TRUTH) FeedYesOrNo(counting,TRUTH);
#define NEWLINE NewLine(counting);
#define FEEDINTEGER(INTEGER) swprintf_s( MQ->messageBuffer, L"%i",INTEGER); FEEDSTRING(MQ->messageBuffer);
#define FEEDFLOAT(FLOAT) swprintf_s( MQ->messageBuffer, L"%f",FLOAT); FEEDSTRING(MQ->messageBuffer);
#define FEEDVECTOR3(XPART,YPART,ZPART) swprintf_s( MQ->messageBuffer, L"(%f, %f, %f)",XPART,YPART,ZPART); FEEDSTRING(MQ->messageBuffer);
#define FEEDVECTOR4(XPART,YPART,ZPART,WPART) swprintf_s( MQ->messageBuffer, L"(%f, %f, %f, %f)",XPART,YPART,ZPART,WPART); FEEDSTRING(MQ->messageBuffer);
#define OneSpace FEEDSTRING(L" ")
#define TwoSpace FEEDSTRING(L"  ")

void CDFPGECtrl::FeedString(bool counting,wchar_t* Text) {
	int i;
	if (counting) {
		m_preCalculatedLength = m_preCalculatedLength + lengthOfString_wide(Text);
	} else {
		i = 0;
		while (Text[i] != 0) {
			StringBuffer[writeIndex] = Text[i]; writeIndex++;
			i++;
		}
	}
}

void CDFPGECtrl::FeedYesOrNo(bool counting,bool Truth) {
	if (counting) {
		if (Truth) {
			m_preCalculatedLength = m_preCalculatedLength + 3;
		} else {
			m_preCalculatedLength = m_preCalculatedLength + 2;
		}
	} else {
		if (Truth) {
			FeedString(counting,L"Yes");
		} else {
			FeedString(counting,L"No");
		}
	}
}

void CDFPGECtrl::NewLine(bool counting) {
	if (counting) {
		m_preCalculatedLength = m_preCalculatedLength + 2;
	} else {
		StringBuffer[writeIndex] = 13; writeIndex++;
		StringBuffer[writeIndex] = 10; writeIndex++;
	}
}

#define TypeHead(TYPE,PLURALNAME) \
	if (DGE.m_numberOf##TYPE > MaxCountPerType) { \
		TwoSpace FEEDINTEGER(DGE.m_numberOf##TYPE) OneSpace FEEDSTRING(L#PLURALNAME) FEEDSTRING(L" are stored.") NEWLINE \
	} else { \
		TYPE##_Struct* Current##TYPE; \
		LoopForward(0,i,DGE.m_numberOf##TYPE - 1) { \
			Current##TYPE = (TYPE##_Struct*)DGE.m_##TYPE.getValue_warning(i); \
			TwoSpace FEEDSTRING(L#TYPE) NEWLINE \
			FEEDSTRING(L"    ID: ") FEEDINTEGER(Current##TYPE->ID) NEWLINE

// External

void CDFPGECtrl::Debug_PrintGeneralData_OutSB(int MaxCountPerType) {
	CRITICAL_CODE_SECTION(
		int i; int j; int Count; DVector3 Vector3Result; btTransform Trans;
		if (!DGE.m_Running) {
			FeedTextToStringBuffer(
				FEEDSTRING(L"The engine is not running.") NEWLINE
				FEEDSTRING(L"Call Engine_Initiate.")
			)
		} else {
			FeedTextToStringBuffer(
				FEEDSTRING(L"Begin") NEWLINE NEWLINE
					FEEDSTRING(L"  Final draw surface") NEWLINE
						FEEDSTRING(L"    ID: 1") NEWLINE
						FEEDSTRING(L"    Current dimensions: ") FEEDINTEGER(DGE.DrawSurface_GetWidth(&(DGE.m_SurfaceScreen))) FEEDSTRING(L"x") FEEDINTEGER(DGE.DrawSurface_GetHeight(&(DGE.m_SurfaceScreen))) NEWLINE
						FEEDSTRING(L"    Number of depth buffers: ") FEEDINTEGER(DGE.m_SurfaceScreen.DepthBuffers) NEWLINE
						FEEDSTRING(L"    Extra color buffer: ") FEEDYESORNO(DGE.m_SurfaceScreen.HaveExtraColorBuffer) NEWLINE
					NEWLINE
					TypeHead(DrawSurface,draw surfaces)
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentDrawSurface->useCount) NEWLINE
						FEEDSTRING(L"    Material use count: ") FEEDINTEGER(CurrentDrawSurface->MaterialCount) NEWLINE
						FEEDSTRING(L"    Current dimensions: ") FEEDINTEGER(DGE.DrawSurface_GetWidth(CurrentDrawSurface)) FEEDSTRING(L"x") FEEDINTEGER(DGE.DrawSurface_GetHeight(CurrentDrawSurface)) NEWLINE
						FEEDSTRING(L"    Auto sized: ") FEEDYESORNO(CurrentDrawSurface->AutoSize) NEWLINE
						if (CurrentDrawSurface->AutoSize) {
							FEEDSTRING(L"      Width multiplier: ") FEEDFLOAT(CurrentDrawSurface->WidthMultiplier) NEWLINE
							FEEDSTRING(L"      Height multiplier: ") FEEDFLOAT(CurrentDrawSurface->HeightMultiplier) NEWLINE
							FEEDSTRING(L"      Width adder: ") FEEDINTEGER(CurrentDrawSurface->WidthAdder) NEWLINE
							FEEDSTRING(L"      Height adder: ") FEEDINTEGER(CurrentDrawSurface->HeightAdder) NEWLINE
						}
						FEEDSTRING(L"    Number of depth buffers: ") FEEDINTEGER(CurrentDrawSurface->DepthBuffers) NEWLINE
						FEEDSTRING(L"    Extra color buffer: ") FEEDYESORNO(CurrentDrawSurface->HaveExtraColorBuffer) NEWLINE
						FEEDSTRING(L"    Has content: ") FEEDYESORNO(CurrentDrawSurface->HasContent) NEWLINE
					}}
					NEWLINE
					TypeHead(CPUSurface,CPU surfaces)
						FEEDSTRING(L"    Dimensions: ") FEEDINTEGER(CurrentCPUSurface->Width) FEEDSTRING(L"x") FEEDINTEGER(CurrentCPUSurface->Height) NEWLINE
					}}
					NEWLINE
					TypeHead(Texture,textures)
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentTexture->useCount) NEWLINE
						FEEDSTRING(L"    Locked: ") FEEDYESORNO(CurrentTexture->Locked) NEWLINE
						FEEDSTRING(L"    Filename: ") FEEDSTRING(CurrentTexture->FileName) NEWLINE
						FEEDSTRING(L"    Dimensions: ") FEEDINTEGER(CurrentTexture->Width) FEEDSTRING(L"x") FEEDINTEGER(CurrentTexture->Height) NEWLINE
					}}
					NEWLINE
					TypeHead(Shader,shaders)
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentShader->useCount) NEWLINE
						FEEDSTRING(L"    Locked: ") FEEDYESORNO(CurrentShader->Locked) NEWLINE
						FEEDSTRING(L"    Compiled: ") FEEDYESORNO(CurrentShader->Compiled) NEWLINE
						FEEDSTRING(L"    Filename: ") FEEDSTRING(CurrentShader->FileName) NEWLINE
						FEEDSTRING(L"    Shader type: ") FEEDSTRING(NameFromShaderType(CurrentShader->ShaderType)) NEWLINE
					}}
					NEWLINE
					TypeHead(RigidBody,rigid bodies)
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentRigidBody->useCount) NEWLINE
						if (CurrentRigidBody->CollisionShape) {
							FEEDSTRING(L"    Collision shape's ID: ") FEEDINTEGER(CurrentRigidBody->CollisionShape->ID) NEWLINE
							FEEDSTRING(L"        Collision shape's type: ") FEEDSTRING(NameFromShapeType(CurrentRigidBody->CollisionShape->ShapeType)) NEWLINE
						}
						FEEDSTRING(L"    Linear mass: ") FEEDFLOAT(CurrentRigidBody->LinearMass) NEWLINE
						FEEDSTRING(L"    Local inertia: ") FEEDVECTOR3(CurrentRigidBody->LocalInertia.x,CurrentRigidBody->LocalInertia.y,CurrentRigidBody->LocalInertia.z) NEWLINE
						FEEDSTRING(L"    Static: ") FEEDYESORNO(CurrentRigidBody->Body->isStaticObject()) NEWLINE
						FEEDSTRING(L"    Kinematic: ") FEEDYESORNO(CurrentRigidBody->Body->isKinematicObject()) NEWLINE
						FEEDSTRING(L"    Active: ") FEEDYESORNO(CurrentRigidBody->Body->isActive()) NEWLINE
					}}
					NEWLINE
					TypeHead(Constraint,constraints)
						FEEDSTRING(L"    Constraint type: ") FEEDSTRING(NameFromConstraintType(CurrentConstraint->ConstraintType)) NEWLINE
						if (CurrentConstraint->BodyA) {
							FEEDSTRING(L"    Body A's ID: ") FEEDINTEGER(CurrentConstraint->BodyA->ID) NEWLINE
						}
						if (CurrentConstraint->BodyB) {
							FEEDSTRING(L"    Body B's ID: ") FEEDINTEGER(CurrentConstraint->BodyB->ID) NEWLINE
						}
					}}
					NEWLINE
					TypeHead(CollisionShape,collision shapes)
						if (!(CurrentCollisionShape->HasValidLocalInertiaPerMass)) {
							CurrentCollisionShape->CollisionShape->calculateLocalInertia(1.0f,CurrentCollisionShape->LocalInertiaPerMass);
							CurrentCollisionShape->HasValidLocalInertiaPerMass = true;
						}
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentCollisionShape->useCount) NEWLINE
						FEEDSTRING(L"    Shape type: ") FEEDSTRING(NameFromShapeType(CurrentCollisionShape->ShapeType)) NEWLINE
						if (CurrentCollisionShape->CPUSurface) {
							FEEDSTRING(L"    CPU surface's ID: ") FEEDINTEGER(CurrentCollisionShape->CPUSurface->ID) NEWLINE
						}
						FEEDSTRING(L"    Local inertia per mass: ") FEEDVECTOR3(CurrentCollisionShape->LocalInertiaPerMass.getX(),CurrentCollisionShape->LocalInertiaPerMass.getY(),CurrentCollisionShape->LocalInertiaPerMass.getZ()) NEWLINE
					}}
					NEWLINE
					TypeHead(Instance,instances)
						FEEDSTRING(L"    Model's ID: ") FEEDINTEGER(CurrentInstance->VisualModel->ID) NEWLINE
						FEEDSTRING(L"        Model's filename: ") FEEDSTRING(CurrentInstance->VisualModel->Model.ModelProperties.FileName) NEWLINE
						if (CurrentInstance->BoneFrame) {
							FEEDSTRING(L"    Bone frame's ID: ") FEEDINTEGER(CurrentInstance->BoneFrame->ID) NEWLINE
						} else {
							FEEDSTRING(L"    No bone frame connected.") NEWLINE
						}
						
						if (CurrentInstance->FollowedRigidBody) {
							FEEDSTRING(L"        Followed rigid body's ID: ") FEEDINTEGER(CurrentInstance->FollowedRigidBody->ID) NEWLINE
							FEEDSTRING(L"            Collision shape's type: ") FEEDSTRING(NameFromShapeType(CurrentInstance->FollowedRigidBody->CollisionShape->ShapeType)) NEWLINE
						} else {
							FEEDSTRING(L"    No rigid body followed.") NEWLINE
						}
						
						FEEDSTRING(L"    Color in RGBA: ") FEEDVECTOR4(CurrentInstance->Color.x,CurrentInstance->Color.y,CurrentInstance->Color.z,CurrentInstance->Color.w) NEWLINE
					}}
					NEWLINE
					TypeHead(Model,models)
						FEEDSTRING(L"    Filename: ") FEEDSTRING(CurrentModel->Model.ModelProperties.FileName) NEWLINE
						FEEDSTRING(L"    Use count: ") FEEDINTEGER(CurrentModel->useCount) NEWLINE
						FEEDSTRING(L"    Filter type: ") FEEDSTRING(CurrentModel->Model.GetFilterName()) NEWLINE
						FEEDSTRING(L"    Bounding shape: ") FEEDSTRING(CurrentModel->Model.GetCullingName()) NEWLINE
						FEEDSTRING(L"    Bound multiplier: ") FEEDFLOAT(CurrentModel->Model.ModelProperties.BoundMultiplier) NEWLINE
						Count = CurrentModel->Model.GetNumberOfParts();
						FEEDSTRING(L"    Number of parts: ") FEEDINTEGER(Count) NEWLINE
						LoopForward(0,j,Count-1){
							FEEDSTRING(L"      Part[") FEEDINTEGER(j) FEEDSTRING(L"]:") NEWLINE
								FEEDSTRING(L"        Name: ") FEEDSTRING(CurrentModel->Model.GetPartName(j)) NEWLINE
								FEEDSTRING(L"        Triangles: ") FEEDINTEGER(CurrentModel->Model.GetTriangleCountFromPart(j)) NEWLINE
						}
						Count = CurrentModel->Model.GetNumberOfBones();
						FEEDSTRING(L"    Number of bones: ") FEEDINTEGER(Count) NEWLINE
						LoopForward(0,j,Count-1){
							FEEDSTRING(L"      Bone[") FEEDINTEGER(j) FEEDSTRING(L"]:") NEWLINE
								FEEDSTRING(L"        Name: ") FEEDSTRING(CurrentModel->Model.GetBoneName(j)) NEWLINE
						}
					}}
					NEWLINE
					TypeHead(Camera,cameras)
						FEEDSTRING(L"    Position: ") FEEDVECTOR3(CurrentCamera->Eye.x,CurrentCamera->Eye.y,CurrentCamera->Eye.z) NEWLINE
						FEEDSTRING(L"    Target: ") FEEDVECTOR3(CurrentCamera->At.x,CurrentCamera->At.y,CurrentCamera->At.z) NEWLINE
						FEEDSTRING(L"    Up vector: ") FEEDVECTOR3(CurrentCamera->Up.x,CurrentCamera->Up.y,CurrentCamera->Up.z) NEWLINE
						FEEDSTRING(L"    Orthogonal: ") FEEDYESORNO(CurrentCamera->IsOrthogonal) NEWLINE
						if (CurrentCamera->IsOrthogonal) {
							FEEDSTRING(L"        Half width: ") FEEDFLOAT(CurrentCamera->OrthogonalHalfWidth) NEWLINE
							FEEDSTRING(L"        Half height: ") FEEDFLOAT(CurrentCamera->OrthogonalHalfHeight) NEWLINE
						} else {
							FEEDSTRING(L"        FOV in radians: ") FEEDFLOAT(CurrentCamera->FieldOfView) NEWLINE
							FEEDSTRING(L"        FOV in degrees: ") FEEDFLOAT(CurrentCamera->FieldOfView * 57.295779513082320876798154814105f) NEWLINE
							FEEDSTRING(L"        Vertical FOV: ") FEEDYESORNO(CurrentCamera->VerticalFieldOfView) NEWLINE
						}
					}}
					NEWLINE
					TypeHead(LightSource,light sources)
						FEEDSTRING(L"    Type: ") FEEDSTRING(NameFromLightType(CurrentLightSource->ShaderData.Type)) NEWLINE
						Vector3Result = CurrentLightSource->ShaderData.Pos; FEEDSTRING(L"    Position: ") FEEDVECTOR3(Vector3Result.x,Vector3Result.y,Vector3Result.z) NEWLINE
						FEEDSTRING(L"    Radius: ") FEEDFLOAT(CurrentLightSource->ShaderData.Radius) NEWLINE
						FEEDSTRING(L"    Color: ") FEEDVECTOR3(CurrentLightSource->ShaderData.Color.x,CurrentLightSource->ShaderData.Color.y,CurrentLightSource->ShaderData.Color.z) NEWLINE
						FEEDSTRING(L"    Near clip: ") FEEDFLOAT(CurrentLightSource->ShaderData.NearClip) NEWLINE
						Vector3Result = CurrentLightSource->ShaderData.XAxis; FEEDSTRING(L"    XAxis: ") FEEDVECTOR3(Vector3Result.x,Vector3Result.y,Vector3Result.z) NEWLINE
						Vector3Result = CurrentLightSource->ShaderData.YAxis; FEEDSTRING(L"    YAxis: ") FEEDVECTOR3(Vector3Result.x,Vector3Result.y,Vector3Result.z) NEWLINE
						Vector3Result = CurrentLightSource->ShaderData.ZAxis; FEEDSTRING(L"    ZAxis: ") FEEDVECTOR3(Vector3Result.x,Vector3Result.y,Vector3Result.z) NEWLINE
						FEEDSTRING(L"    Texture altas rect: ") FEEDVECTOR4(CurrentLightSource->ShaderData.TextureAtlasRect.x,CurrentLightSource->ShaderData.TextureAtlasRect.y,CurrentLightSource->ShaderData.TextureAtlasRect.z,CurrentLightSource->ShaderData.TextureAtlasRect.w) NEWLINE
						FEEDSTRING(L"    Shadow transparency: ") FEEDFLOAT(CurrentLightSource->ShaderData.ShadowTransparency) NEWLINE
						FEEDSTRING(L"    Can cast shadows: ") FEEDYESORNO(CurrentLightSource->WantDepthTile) NEWLINE
						FEEDSTRING(L"    Has shadow allocation: ") FEEDYESORNO(CurrentLightSource->HasDepthTile) NEWLINE
						FEEDSTRING(L"    Depth altas rect: ") FEEDVECTOR4(CurrentLightSource->ShaderData.DepthAtlasRect.x,CurrentLightSource->ShaderData.DepthAtlasRect.y,CurrentLightSource->ShaderData.DepthAtlasRect.z,CurrentLightSource->ShaderData.DepthAtlasRect.w) NEWLINE
						if (CurrentLightSource->IsOrthogonal) {
							FEEDSTRING(L"    HalfWidth: ") FEEDFLOAT(CurrentLightSource->ShaderData.WidthSlope) NEWLINE
							FEEDSTRING(L"    HalfHeight: ") FEEDFLOAT(CurrentLightSource->ShaderData.HeightSlope) NEWLINE
						} else {
							FEEDSTRING(L"    Width slope: ") FEEDFLOAT(CurrentLightSource->ShaderData.WidthSlope) NEWLINE
							FEEDSTRING(L"    Height slope: ") FEEDFLOAT(CurrentLightSource->ShaderData.HeightSlope) NEWLINE
						}
					}}
					NEWLINE
					TypeHead(SoundBuffer,sound buffers)
						FEEDSTRING(L"    Is 3D sound: ") FEEDYESORNO(CurrentSoundBuffer->Buffer->Flag_3DSound) NEWLINE
						FEEDSTRING(L"    Editable: ") FEEDYESORNO(CurrentSoundBuffer->Buffer->Flag_IsEditable) NEWLINE
						FEEDSTRING(L"    Original sample rate: ") FEEDINTEGER(CurrentSoundBuffer->Buffer->GetSampleRate()) NEWLINE
						FEEDSTRING(L"    Number of channels: ") FEEDINTEGER(CurrentSoundBuffer->Buffer->GetNumberOfChannels()) NEWLINE
						FEEDSTRING(L"    Samples per channel: ") FEEDINTEGER(CurrentSoundBuffer->Buffer->GetSamplesPerChannel()) NEWLINE
						Count = CurrentSoundBuffer->Buffer->Flag_NumberOfCopies;
						FEEDSTRING(L"    Number of copies: ") FEEDINTEGER(Count) NEWLINE
						LoopForward(0,j,Count-1){
							FEEDSTRING(L"      Copy[") FEEDINTEGER(j) FEEDSTRING(L"]:") NEWLINE
							FEEDSTRING(L"        Playing: ") FEEDYESORNO(CurrentSoundBuffer->Buffer->IsPlaying(j)) NEWLINE
							FEEDSTRING(L"        Speed: ") FEEDFLOAT(CurrentSoundBuffer->Buffer->GetSpeed(j)) NEWLINE
							FEEDSTRING(L"        Volume: ") FEEDFLOAT(CurrentSoundBuffer->Buffer->GetVolume(j)) NEWLINE
							if (CurrentSoundBuffer->Buffer->Flag_3DSound) {
								Vector3Result = CurrentSoundBuffer->Buffer->Get3DPosition(j);
								FEEDSTRING(L"        Position: ") FEEDVECTOR3(Vector3Result.x,Vector3Result.y,Vector3Result.z) NEWLINE
							}
						}
					}}
					NEWLINE
					TypeHead(BoneFrame,bone frames)
					}}
				NEWLINE FEEDSTRING(L"End")
			)
		}
	)
}

int CDFPGECtrl::Debug_GetLastNumberOfVisibleLightSources(void) {
	int Result;
	CRITICAL_CODE_SECTION(
		if (DGE.m_Running) {
			Result = DGE.m_SeenLightSources;
		} else {
			REPORT_NOT_RUNNING(L"Debug_GetLastNumberOfVisibleLightSources")
			Result = 0;
		}
	)
	return Result;
}

int CDFPGECtrl::Debug_GetLastNumberOfCulledLightSources(void) {
	int Result;
	CRITICAL_CODE_SECTION(
		if (DGE.m_Running) {
			Result = DGE.m_CulledLightSources;
		} else {
			REPORT_NOT_RUNNING(L"Debug_GetLastNumberOfCulledLightSources")
			Result = 0;
		}
	)
	return Result;
}

int CDFPGECtrl::Debug_GetLastNumberOfVisibleInstances(void) {
	int Result;
	CRITICAL_CODE_SECTION(
		if (DGE.m_Running) {
			Result = DGE.m_SeenInstances;
		} else {
			REPORT_NOT_RUNNING(L"Debug_GetLastNumberOfVisibleInstances")
			Result = 0;
		}
	)
	return Result;
}

int CDFPGECtrl::Debug_GetLastNumberOfCulledInstances(void) {
	int Result;
	CRITICAL_CODE_SECTION(
		if (DGE.m_Running) {
			Result = DGE.m_CulledInstances;
		} else {
			REPORT_NOT_RUNNING(L"Debug_GetLastNumberOfCulledInstances")
			Result = 0;
		}
	)
	return Result;
}

float CDFPGECtrl::Debug_GetPartAllocatedOfDepthAtlas(void) {
	float Result;
	CRITICAL_CODE_SECTION(
		Result = DGE.m_DepthAtlas_PartUsed;
	)
	return Result;
}

int CDFPGECtrl::Debug_WasDepthAtlasAllocationDenied(void){
	int Result;
	CRITICAL_CODE_SECTION(
		Result = DGE.m_DepthAtlas_DeniedAllocation;
	)
	return Result;
}
