#include "MLRHeaders.hpp"

//#############################################################################
//#####################    MLRIndexedPrimitiveBase    #########################
//#############################################################################

DynamicArrayOf<WORD>
	*MLRIndexedPrimitiveBase::clipExtraIndex;

MLRIndexedPrimitiveBase::ClassData*
	MLRIndexedPrimitiveBase::DefaultData = NULL;

WORD
	*indexOffset;

Stuff::DynamicArrayOf<BYTE>
	*MLRIndexedPrimitiveBase::visibleIndexedVertices = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRIndexedPrimitiveBase::InitializeClass()
{
	Verify(!DefaultData);
	Verify(gos_GetCurrentHeap() == StaticHeap);
	DefaultData =
		new ClassData(
			MLRIndexedPrimitiveBaseClassID,
			"MidLevelRenderer::MLRIndexedPrimitiveBase",
			MLRPrimitiveBase::DefaultData,
			NULL
		);
	Register_Object(DefaultData);
	
	Verify(!clipExtraIndex);
	clipExtraIndex = new DynamicArrayOf<WORD> (Limits::Max_Size_Of_Index_Buffer);
	Check_Object(clipExtraIndex);

	Verify(!indexOffset);
	indexOffset = new WORD [Limits::Max_Size_Of_Index_Buffer];
	Check_Pointer(indexOffset);

	Verify(!visibleIndexedVertices);
	visibleIndexedVertices = new DynamicArrayOf<BYTE>(Limits::Max_Number_Vertices_Per_Mesh);
	Check_Object(visibleIndexedVertices);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRIndexedPrimitiveBase::TerminateClass()
{
	Check_Object(visibleIndexedVertices);
	delete visibleIndexedVertices;
	visibleIndexedVertices = NULL;

	Check_Pointer(indexOffset);
	delete [] indexOffset;
	indexOffset = NULL;

	Check_Object(clipExtraIndex);
	delete clipExtraIndex;
	clipExtraIndex = NULL;

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRIndexedPrimitiveBase::MLRIndexedPrimitiveBase(
	ClassData *class_data,
	MemoryStream *stream,
	int version
):
	MLRPrimitiveBase(class_data, stream, version)
{
	Check_Pointer(this);
	Check_Object(stream);

	switch(version)
	{
		case 1:
		case 2:
		{
			STOP(("This class got created only after version 2 !"));
		}
		break;
		default:
		{
			// Convert WORD indices to BYTE before version 18
			if (version<18)
			{
				DynamicArrayOf<WORD> index_temp;
				MemoryStreamIO_Read(stream, &index_temp);
				if (dataStore == NULL) 
				{
					dataStore = new DataStorage;
				}

				if((index_temp.GetLength()) % 3 != 0)
				{
					STOP(("Indices are not modula of 3"));
				}
				int len = index_temp.GetLength() / 3;

				dataStore->index.SetLength(index_temp.GetLength());

				int i, j;
				for (i=0,j=0; i<len; ++i)
				{
					if(
						index_temp[3*i] < Limits::Max_Number_Vertices_Per_Mesh &&
						index_temp[3*i+1] < Limits::Max_Number_Vertices_Per_Mesh &&
						index_temp[3*i+2] < Limits::Max_Number_Vertices_Per_Mesh
					)
					{
						dataStore->index[3*j] = (BYTE) (index_temp[3*i] & 0xff);
						dataStore->index[3*j+1] = (BYTE) (index_temp[3*i+1] & 0xff);
						dataStore->index[3*j+2] = (BYTE) (index_temp[3*i+2] & 0xff);

						j++;
					}
				}

				dataStore->index.SetLength(3*j);

				index.AssignData(dataStore->index.GetData(), dataStore->index.GetLength());
			}
			else 
				MemoryStreamIO_Read(stream, &index);
		}
		break;
	}

	visibleIndexedVerticesKey = false;
}

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

	MLRPrimitiveBase::Save(stream);

	MemoryStreamIO_Write(stream, &index);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRIndexedPrimitiveBase::MLRIndexedPrimitiveBase(ClassData *class_data):
	MLRPrimitiveBase(class_data)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRIndexedPrimitiveBase::~MLRIndexedPrimitiveBase()
{
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRIndexedPrimitiveBase::InitializeDrawPrimitive(BYTE vis, int parameter)
{
	MLRPrimitiveBase::InitializeDrawPrimitive(vis, parameter);

	gos_indices = NULL;
	numGOSIndices = -1;

	visibleIndexedVerticesKey = false;
	int i, len = coords.GetLength();

	for(i=0;i<len;i++)
	{
		(*visibleIndexedVertices)[i] = 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRIndexedPrimitiveBase::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
	MLRIndexedPrimitiveBase::SetIndexData(
		const BYTE *index_array,
		int index_count
	)
{
	Check_Object(this); 
	Check_Pointer(index_array);

#ifdef _ARMOR
	int len = coords.GetLength();
	for(int i=0;i<index_count;i++)
	{
		Verify(index_array[i] < len);
	}
#endif

	index.AssignData(index_array, index_count);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRIndexedPrimitiveBase::GetIndexData(
		const BYTE **index_array,
		int *index_count
	)
{
	Check_Object(this); 

	*index_array = index.GetData();
	*index_count = index.GetLength();
}

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

	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
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRIndexedPrimitiveBase::FogMesh(
		const Stuff::LinearMatrix4D *worldToShape,
		const Stuff::Point3D *cameraPos,
		FogData *fogData
	)
{
	Check_Object(this);

	MLR_RENDER("Fog");
	Start_Timer(Fog_Time);

	int fogMode = state.GetFogMode();
	if(fogMode == 0)
	{
		channelUse &= ~(1<<FogChannel);
		Stop_Timer(Fog_Time);
		return false;
	}
	fogMode--;

	if(visibleIndexedVerticesKey == false)
	{
		FindVisibleVertices();
	}


#if 1
	fogData->cameraPos = *cameraPos;
	fogData->near_fog = fogTable[fogMode][0];
	fogData->far_fog = fogTable[fogMode][1];
	fogData->one_over = fogTable[fogMode][3];

	fogData->height_low_fog = fogTable[Limits::Max_Number_Of_FogStates-1][0];
	fogData->height_top_fog = fogTable[Limits::Max_Number_Of_FogStates-1][1];
	fogData->height_one_over = fogTable[Limits::Max_Number_Of_FogStates-1][3];

	fogData->height_fog_density0 = 255.0f*fogTable[Limits::Max_Number_Of_FogStates-1][2];
	fogData->height_fog_density = static_cast<BYTE>(0xff - fogData->height_fog_density0);
	fogData->height_fog_density0 *= fogData->height_one_over;

	Point3D org(0.0f, 0.0f, 0.0f);
	fogData->orgInShape.Multiply(org, *worldToShape);
	Vector3D up(0.0f, 1.0f, 0.0f);
	fogData->upInShape.Multiply(up, *worldToShape);
#else
	Vector3D v;
	Point3D orgInShape, org(0.0f, 0.0f, 0.0f);
	Vector3D upInShape, up(0.0f, 1.0f, 0.0f);
	orgInShape.Multiply(org, *worldToShape);
	upInShape.Multiply(up, *worldToShape);

	int len = coords.GetLength();
	Verify(len<extraChannels[2*FogChannel]->GetLength());
	BYTE *data = extraChannels[2*FogChannel]->GetData();
	Scalar n = fogTable[fogMode][0];
	Scalar f = fogTable[fogMode][1];
	Scalar o = fogTable[fogMode][3];

	Scalar hn = fogTable[Limits::Max_Number_Of_FogStates-1][0];
	Scalar hf = fogTable[Limits::Max_Number_Of_FogStates-1][1];
	Scalar ho = fogTable[Limits::Max_Number_Of_FogStates-1][3];

	Scalar hden0 = 255.0f*fogTable[Limits::Max_Number_Of_FogStates-1][2];
	BYTE hden = static_cast<BYTE>(0xff - hden0);
	hden0 *= ho;

	for(int i=0;i<len;i++)
	{
		if((*visibleIndexedVertices)[i] == 0)
		{    
			continue;
		}

		v.Subtract(coords[i], *cameraPos);
		Scalar d = v.GetApproximateLength();
		BYTE df = (BYTE)( (d<n+SMALL) ? 0xff : (d+SMALL>f) ? 0 : Truncate_Float_To_Byte(255.0f*(1.0f - (d-n)*o)) );

		BYTE hhf;
		if(hden < 0xff)
		{
			v.Subtract(coords[i], orgInShape);
			Scalar h = v*upInShape;
			hhf = (BYTE)( (h<hn+SMALL) ? hden : (h+SMALL>hf) ? 0xff : Truncate_Float_To_Byte(hden + hden0*(h-hn)) );
			data[i] = df < hhf ? df : hhf;
		}
		else
		{
			data[i] = df;
		}
	}
#endif

	channelUse |= 1<<FogChannel;

	Stop_Timer(Fog_Time);
	
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRIndexedPrimitiveBase::CheckIndicies()
{
	for(int i=0;i<numGOSIndices;i++)
	{
		if(gos_indices[i] >= numGOSVertices)
		{
			STOP(("Invalid indicies detected !"));
		}
	}
	return true;
}

