#include "MLRHeaders.hpp"

Stuff::ReadOnlyArrayOf<BYTE> MidLevelRenderer::temp_lengths;

int clipTrick[6][2] = 
{
	{ 1, 1},
	{ 1, 0},
	{ 0, 1},
	{ 0, 0},
	{ 2, 0},
	{ 2, 1}
};


//#############################################################################
//#########################    ClipPolygon2    ############################
//#############################################################################

void ClipPolygon2::Init(int passes)
{
	Verify(gos_GetCurrentHeap() == StaticHeap);
	coords.SetLength(Limits::Max_Number_Vertices_Per_Polygon);
	colors.SetLength(Limits::Max_Number_Vertices_Per_Polygon);
	texCoords.SetLength(passes*Limits::Max_Number_Vertices_Per_Polygon);
	clipPerVertex.SetLength(Limits::Max_Number_Vertices_Per_Polygon);

	for(int i=0;i<MLRPrimitiveBase::MaxNumberOfExtraChannel;i++)
	{
		extraChannel[i].SetLength(Limits::Max_Number_Vertices_Per_Polygon);
	}
}

void ClipPolygon2::Destroy()
{
	coords.SetLength(0);
	colors.SetLength(0);
	texCoords.SetLength(0);
	clipPerVertex.SetLength(0);

	for(int i=0;i<MLRPrimitiveBase::MaxNumberOfExtraChannel;i++)
	{
		extraChannel[i].SetLength(0);
	}
}

//#############################################################################
//#########################    MLRPrimitiveBase    ############################
//#############################################################################

MLRPrimitiveBase::ClassData*
	MLRPrimitiveBase::DefaultData = NULL;

DynamicArrayOf<Vector4D>
	*MLRPrimitiveBase::transformedCoords;

DynamicArrayOf<MLRClippingState>
	*MLRPrimitiveBase::clipPerVertex;
DynamicArrayOf<Vector4D>
	*MLRPrimitiveBase::clipExtraCoords;

DynamicArrayOf<Vector2DScalar>
	*MLRPrimitiveBase::clipExtraTexCoords;

DynamicArrayOf<ColorType>
	MLR_I_C_PMesh::clipExtraColors;

DynamicArrayOf<WORD>
	*MLRPrimitiveBase::clipExtraLength;

ClipPolygon2
	*MLRPrimitiveBase::clipBuffer;

DynamicArrayOf<BYTE>
	*MLRPrimitiveBase::extraChannels[2*MaxNumberOfExtraChannel];

Scalar
	MLRPrimitiveBase::fogTable[Limits::Max_Number_Of_FogStates][4];

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::InitializeClass()
{
	Verify(!DefaultData);
	Verify(gos_GetCurrentHeap() == StaticHeap);
	DefaultData =
		new ClassData(
			MLRPrimitiveBaseClassID,
			"MidLevelRenderer::MLRPrimitiveBase",
			RegisteredClass::DefaultData,
			NULL
		);
	Register_Object(DefaultData);

	transformedCoords = new DynamicArrayOf<Vector4D> (Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(transformedCoords);

	clipPerVertex = new DynamicArrayOf<MLRClippingState> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipPerVertex);
	clipExtraCoords = new DynamicArrayOf<Vector4D> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraCoords);
	clipExtraTexCoords = new DynamicArrayOf<Vector2DScalar> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraTexCoords);

//	start - fix for bug #5367
//	array overwrite - extra channels only was set to Max_Number_Vertices_Per_Mesh, I doubled this number
//	my buddy: Paul Tozour
	clipExtraColors = new DynamicArrayOf<ColorType>(2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraColors);

	clipExtraLength = new DynamicArrayOf<WORD> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraLength);

	clipBuffer = new ClipPolygon2 [2];
	Register_Pointer(clipBuffer);

	clipBuffer[0].Init(Limits::Max_Number_Of_Multitextures);
	clipBuffer[1].Init(Limits::Max_Number_Of_Multitextures);

	for(int i=0;i<2*MLRPrimitiveBase::MaxNumberOfExtraChannel;i++)
	{
		extraChannels[i] = new DynamicArrayOf<BYTE> (2*Limits::Max_Number_Vertices_Per_Mesh);
		Register_Pointer(extraChannels[i]);
	}
// end - fix for bug #5367
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::TerminateClass()
{
	for(int i=0;i<2*MLRPrimitiveBase::MaxNumberOfExtraChannel;i++)
	{
		Unregister_Object(extraChannels[i]);
		delete extraChannels[i];
	}

	clipBuffer[1].Destroy();
	clipBuffer[0].Destroy();
	Unregister_Pointer(clipBuffer);
	delete [] clipBuffer;

	Unregister_Object(transformedCoords);
	delete transformedCoords;

	Unregister_Object(clipPerVertex);
	delete clipPerVertex;
	Unregister_Object(clipExtraCoords);
	delete clipExtraCoords;
	Unregister_Object(clipExtraTexCoords);
	delete clipExtraTexCoords;
	Unregister_Object(clipExtraColors);
	delete clipExtraColors;
	Unregister_Object(clipExtraLength);
	delete clipExtraLength;


	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRPrimitiveBase::MLRPrimitiveBase(
	ClassData *class_data,
	MemoryStream *stream,
	int version
):
	RegisteredClass(class_data)
{
	Check_Pointer(this);
	Check_Object(stream);
	dataStore = NULL;

	switch(version)
	{
		case 1:
		case 2:
		{
			STOP(("This class got created only after version 2 !"));
		}
		break;
		default:
		{
			bool too_big = false;

			MemoryStreamIO_Read(stream, &coords);

			MemoryStreamIO_Read(stream, &texCoords);

			if (coords.GetLength()>Limits::Max_Number_Vertices_Per_Mesh)
			{
				too_big = true;
				coords.AssignData(coords.GetData(), Limits::Max_Number_Vertices_Per_Mesh);
				texCoords.AssignData(texCoords.GetData(), Limits::Max_Number_Vertices_Per_Mesh);
			}

			if(version<15)
			{
				MemoryStreamIO_Read(stream, &temp_lengths);
				int dMode;
				*stream >> dMode;
				drawMode = static_cast<BYTE>(dMode);
			}
			else
			{
				*stream >> drawMode;
				temp_lengths.AssignData(NULL, 0);
			}

			gos_PushCurrentHeap(StatesHeap);
			referenceState.Load(stream, version);
			gos_PopCurrentHeap();

#if defined (_DEBUG) && 0
			if(too_big == true)
			{
				MLRTexture *tex = (*MLRTexturePool::Instance)[&referenceState];
				const char *tex_name = NULL;
				if(tex!=NULL)
				{
					tex_name = tex->GetTextureName();
				}
				if(tex_name!=NULL)
				{
					PAUSE(("Mesh with texture \"%s\" currently loaded has more than %d vertices", tex_name, Limits::Max_Number_Vertices_Per_Mesh));
				}
				else
				{
					PAUSE(("Mesh currently loaded has more than %d vertices", Limits::Max_Number_Vertices_Per_Mesh));
				}
			}
#endif
		}
		break;
	}

	passes = 1;
	referenceCount = 1;
	channelUse = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::Save(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

	*stream << GetClassID();

	MemoryStreamIO_Write(stream, &coords);

	MemoryStreamIO_Write(stream, &texCoords);

	*stream << drawMode;

	referenceState.Save(stream);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRPrimitiveBase::MLRPrimitiveBase(ClassData *class_data):
	RegisteredClass(class_data)
{
	referenceState = 0;
	
	state = 0;

	passes = 1;

	referenceCount = 1;

	dataStore = new DataStorage;

	channelUse = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRPrimitiveBase::~MLRPrimitiveBase()
{
	Verify(referenceCount==0);
	if (dataStore)
		delete dataStore;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::InitializeDraw()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::InitializeDrawPrimitive(BYTE vis, int)
{
	gos_vertices = NULL;
	numGOSVertices = -1;

	visible = vis;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::SetCoordData(
		const Point3D *data,
		int dataSize
	)
{
	Check_Object(this); 
	Check_Pointer(data);

	Verify(texCoords.GetLength() == 0 || dataSize == texCoords.GetLength());

#if defined (MAX_NUMBER_VERTICES)
	Verify(dataSize <= MAX_NUMBER_VERTICES);
#endif
	coords.AssignData(data, dataSize);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::GetCoordData(
		const Point3D **data,
		int *dataSize
	)
{
	Check_Object(this);

	*data = coords.GetData();
	*dataSize = coords.GetLength();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::SetTexCoordData(
		const Vector2DScalar *data,
		int dataSize
	)
{
	Check_Object(this); 
	Check_Pointer(data);

	Verify(coords.GetLength() == 0 || dataSize == coords.GetLength());

	texCoords.AssignData((Vector2DScalar *)data, dataSize);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::GetTexCoordData(
		const Vector2DScalar **data,
		int *dataSize
	)
{
	Check_Object(this); 
	*data = texCoords.GetData();
	*dataSize = texCoords.GetLength();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::Transform(Matrix4D *mat)
{
	Check_Object(this);

	MLR_RENDER("Transform::MLRPrimitiveBase");
	Start_Timer(Transform_Time);
	int i, len = coords.GetLength();

	for(i=0;i<len;i++)
	{
		(*transformedCoords)[i].Multiply(coords[i], *mat);
	}

	#ifdef LAB_ONLY
		Set_Statistic(TransformedVertices, TransformedVertices+len);
	#endif
	Stop_Timer(Transform_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::SetFogTableEntry(
		int entry,
		Scalar fogNearClip,
		Scalar fogFarClip,
		Scalar fogDensity
	)
{
	Verify(fogFarClip>fogNearClip);
	Verify(entry>0 && entry<=Limits::Max_Number_Of_FogStates);
#if FOG_HACK
	GOSVertex::SetFogTableEntry(entry, fogNearClip, fogFarClip, fogDensity);
#endif

	entry--;
	Verify(entry>=0);

	fogTable[entry][0] = fogNearClip;
	fogTable[entry][1] = fogFarClip;
	fogTable[entry][2] = fogDensity;

	if(Close_Enough(fogFarClip, fogNearClip))
	{
		#if defined(LAB_ONLY)
			PAUSE((0, "FogNearClip: %f and FogFarClip: %f too close to each other!", fogNearClip, fogFarClip));
		#endif
		fogTable[entry][3] = 1.0f/500.0f;
	}
	else
	{
		fogTable[entry][3] = 1.0f/(fogFarClip-fogNearClip);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
		MLRPrimitiveBase::GetFogTableEntry(int entry, Stuff::Scalar * pfogNearClip, Stuff::Scalar * pfogFarClip, Stuff::Scalar * pfogDensity)
{
	Verify(entry>0 && entry<=Limits::Max_Number_Of_FogStates);

	entry--;
	*pfogNearClip                   = fogTable[entry][0];
	*pfogFarClip                    = fogTable[entry][1];
	*pfogDensity                    = fogTable[entry][2];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRPrimitiveBase::GetExtend(Stuff::ExtentBox *box)
{
	Check_Object(this);
	Check_Object(box);

	if(coords.GetLength()==0)
	{
		return;
	}

	box->minX = box->maxX = coords[0].x;
	box->minY = box->maxY = coords[0].y;
	box->minZ = box->maxZ = coords[0].z;

	for(int i=0;i<coords.GetLength();i++)
	{
		if(coords[i].x < box->minX)
		{
			box->minX = coords[i].x;
		}
		if(coords[i].y < box->minY)
		{
			box->minY = coords[i].y;
		}
		if(coords[i].z < box->minZ)
		{
			box->minZ = coords[i].z;
		}
		if(coords[i].x > box->maxX)
		{
			box->maxX = coords[i].x;
		}
		if(coords[i].y > box->maxY)
		{
			box->maxY = coords[i].y;
		}
		if(coords[i].z > box->maxZ)
		{
			box->maxZ = coords[i].z;
		}
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRPrimitiveBase::CastRay(
		Line3D *line,
		Normal3D *normal
	)
{
	Check_Object(this);
	Check_Object(line);
	Check_Pointer(normal);
	STOP(("Not implemented"));
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRShape*
	MidLevelRenderer::CreateIndexedIcosahedron(IcoInfo& icoInfo, Stuff::DynamicArrayOf<MLRState> *states)
{
	switch(icoInfo.type)
	{
		case MLR_I_PMeshClassID:
			return CreateIndexedIcosahedron_NoColor_NoLit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_C_PMeshClassID:
			return CreateIndexedIcosahedron_Color_NoLit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_L_PMeshClassID:
			return CreateIndexedIcosahedron_Color_Lit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_DT_PMeshClassID:
			return CreateIndexedIcosahedron_NoColor_NoLit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_C_DT_PMeshClassID:
			return CreateIndexedIcosahedron_Color_NoLit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_L_DT_PMeshClassID:
			return CreateIndexedIcosahedron_Color_Lit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_DeT_PMeshClassID:
			return CreateIndexedIcosahedron_NoColor_NoLit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_C_DeT_PMeshClassID:
			return CreateIndexedIcosahedron_Color_NoLit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_L_DeT_PMeshClassID:
			return CreateIndexedIcosahedron_Color_Lit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_TMeshClassID:
			return CreateIndexedTriIcosahedron_NoColor_NoLit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_C_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_NoLit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_L_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_Lit(icoInfo, &(*states)[0]);
		break;
		case MLR_I_DeT_TMeshClassID:
			return CreateIndexedTriIcosahedron_NoColor_NoLit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_C_DeT_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_NoLit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_L_DeT_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_Lit_DetTex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_DT_TMeshClassID:
			return CreateIndexedTriIcosahedron_NoColor_NoLit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_C_DT_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_NoLit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
		case MLR_I_L_DT_TMeshClassID:
			return CreateIndexedTriIcosahedron_Color_Lit_2Tex(icoInfo, &(*states)[0], &(*states)[1]);
		break;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *
	MidLevelRenderer::IcoInfo::GetTypeName()
{
	switch(type)
	{
		case MLR_I_PMeshClassID:
			return "not colored, unlit mesh";
		break;
		case MLR_I_C_PMeshClassID:
			return "colored, unlit mesh";
		break;
		case MLR_I_L_PMeshClassID:
			return "colored, lit mesh";
		break;
		case MLR_I_DT_PMeshClassID:
			return "not colored, unlit mesh w/ 2 Tex";
		break;
		case MLR_I_C_DT_PMeshClassID:
			return "colored, unlit mesh w/ 2 Tex";
		break;
		case MLR_I_L_DT_PMeshClassID:
			return "colored, lit mesh w/ 2 Tex";
		break;
		case MLR_I_MT_PMeshClassID:
			return "not colored, unlit mesh w/ 4 Tex";
		break;
		case MLR_I_DeT_PMeshClassID:
			return "not colored, unlit mesh w/ DetTex";
		break;
		case MLR_I_C_DeT_PMeshClassID:
			return "colored, unlit mesh w/ DetTex";
		break;
		case MLR_I_L_DeT_PMeshClassID:
			return "colored, lit mesh w/ DetTex";
		break;
		case MLR_I_TMeshClassID:
			return "not colored, unlit tri. mesh";
		break;
		case MLR_I_C_TMeshClassID:
			return "colored, unlit tri. mesh";
		break;
		case MLR_I_L_TMeshClassID:
			return "colored, lit tri. mesh";
		break;
		case MLR_I_DeT_TMeshClassID:
			return "not colored, unlit tri. mesh w/ DetTex";
		break;
		case MLR_I_C_DeT_TMeshClassID:
			return "colored, unlit tri. mesh w/ DetTex";
		break;
		case MLR_I_L_DeT_TMeshClassID:
			return "colored, lit tri. mesh w/ DetTex";
		break;
		case MLR_I_DT_TMeshClassID:
			return "not colored, unlit tri. mesh w/ 2 Tex";
		break;
		case MLR_I_C_DT_TMeshClassID:
			return "colored, unlit tri. mesh w/ 2 Tex";
		break;
		case MLR_I_L_DT_TMeshClassID:
			return "colored, lit tri. mesh w/ 2 Tex";
		break;
	}
	return "mesh";
}

//#############################################################################
//#####################    MLRPrimitiveBase__ClassData    #####################
//#############################################################################

void
	MLRPrimitiveBase__ClassData::TestInstance()
{
	Verify(IsDerivedFrom(MLRPrimitiveBase::DefaultData));
}
