#include "../../Scene/Terrain/RuWorld_Water.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(CRuWorld_Water, IRuEntity_Renderable)
ruHEAP_DEFINE_SUBCLASS(CRuWorld_Water, "CRuWorld_Water", "IRuEntity_Renderable")
ruCLASSGUID_DEFINE(CRuWorld_Water, 0x9E1F53AC, 0xD1414667, 0x87C43210, 0x4CEDB85F)

// ************************************************************************************************************************************************************

CRuWorld_Water::CRuWorld_Water()
{
	m_waterBodyGUID = CRuGUID::Invalid;

	m_waterHF = NULL;
	m_waterCF = NULL;
	m_waterFlags = NULL;

	m_waterMesh = NULL;
	m_waterMaterial = ruNEW CRuMaterial();
	m_waterMaterial->SetShader(g_ruResourceManager->GetShader("ruSHADER_NULL"));
}

CRuWorld_Water::~CRuWorld_Water()
{
	ruSAFE_RELEASE(m_waterHF);
	ruSAFE_RELEASE(m_waterCF);
	ruSAFE_RELEASE(m_waterFlags);

	ruSAFE_RELEASE(m_waterMesh);
	ruSAFE_RELEASE(m_waterMaterial);
}

IRuObject *CRuWorld_Water::Clone(IRuObject *clonedObject)
{
	// Create a new cloned entity if necessary
	clonedObject = clonedObject ? clonedObject : ruNEW CRuWorld_Water();
	CRuWorld_Water *waterLayer = static_cast<CRuWorld_Water *>(clonedObject);

	// Chain cloning call to parent class
	IRuEntity_Renderable::Clone(clonedObject);

	// Clone data fields
	waterLayer->m_waterBodyGUID = m_waterBodyGUID;
	waterLayer->m_waterHF = static_cast<CRuMojo_AttributeParcel<REAL> *>(m_waterHF->Clone());
	waterLayer->m_waterCF = static_cast<CRuMojo_AttributeParcel<RUCOLOR> *>(m_waterCF->Clone());
	waterLayer->m_waterFlags = static_cast<CRuMojo_AttributeParcel<BYTE> *>(m_waterFlags->Clone());

	// Build mesh for the cloned water layer
	waterLayer->BuildWaterLayerMesh();

	// Set material
	if(m_waterMaterial)
		waterLayer->SetWaterLayerMaterial(m_waterMaterial);

	return clonedObject;
}

BOOL CRuWorld_Water::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read version
	INT32 version;
	stream->Read(&version, sizeof(INT32));

	switch(version)
	{
		case 1:
			{
				// Read water body GUID
				RuReadGUID(stream, m_waterBodyGUID);

				// Read height field
				Mojo_SerializeFrom(&m_waterHF, stream);

				// Read flag field
				m_waterFlags = ruNEW CRuMojo_AttributeParcel<BYTE>();
				m_waterFlags->SerializeFrom(stream);

				m_waterCF = ruNEW CRuMojo_AttributeParcel<RUCOLOR>(m_waterFlags->GetParcelWidth(), m_waterFlags->GetParcelHeight());
				m_waterCF->SetParcelOrigin(m_waterFlags->GetParcelOrigin());
				m_waterCF->SetParcelOverlapGap(m_waterFlags->GetParcelOverlapGap());
				m_waterCF->SetParcelSpacing(m_waterFlags->GetParcelSpacing());
			}

			break;

		case 2:
			{
				// Read water body GUID
				RuReadGUID(stream, m_waterBodyGUID);

				// Read height field
				Mojo_SerializeFrom(&m_waterHF, stream);

				// Read color field
				m_waterCF = ruNEW CRuMojo_AttributeParcel<RUCOLOR>();
				m_waterCF->SerializeFrom(stream);

				// Read flag field
				m_waterFlags = ruNEW CRuMojo_AttributeParcel<BYTE>();
				m_waterFlags->SerializeFrom(stream);
			}

			break;
	}

	// Build mesh for the cloned water layer
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::SerializeTo(IRuStream *stream)
{
	INT32 version = 2;

	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write version
	stream->Write(&version, sizeof(INT32));

	// Write water body GUID
	stream->Write(m_waterBodyGUID.Data(), m_waterBodyGUID.Size());

	// Write height field
	Mojo_SerializeTo(m_waterHF, stream);

	// Write color field
	m_waterCF->SerializeTo(stream);

	// Write flag field
	m_waterFlags->SerializeTo(stream);

	return TRUE;
}

INT32 CRuWorld_Water::GetNumMeshes() const
{
	return 1;
}

BOOL CRuWorld_Water::GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut)
{
	if(meshIndex == 0)
	{
		*meshOut = m_waterMesh;
		*materialOut = m_waterMaterial;

		return TRUE;
	}

	return FALSE;
}

BOOL CRuWorld_Water::QueryCollision(CRuCollisionQuery *colQuery)
{
	if(m_waterMesh)
	{
		return m_waterMesh->QueryCollision(colQuery);
	}

	return FALSE;
}

BOOL CRuWorld_Water::Allocate(const CRuGUID &waterBodyGUID, const CRuVector3 &layerOrigin, INT32 dimension, REAL gridSpacing, INT32 overlapGap)
{
	m_waterBodyGUID = waterBodyGUID;
	m_waterHF = ruNEW CRuMojo_AttributeParcel<REAL>(dimension + 2 * overlapGap, dimension + 2 * overlapGap);
	m_waterCF = ruNEW CRuMojo_AttributeParcel<RUCOLOR>(dimension, dimension);
	m_waterFlags = ruNEW CRuMojo_AttributeParcel<BYTE>(dimension, dimension);

	// Set water height field parameters
	m_waterHF->SetParcelOrigin(CRuVector3(layerOrigin.m_x - gridSpacing * overlapGap, 0.0f, layerOrigin.m_z - gridSpacing * overlapGap));
	m_waterHF->SetParcelSpacing(gridSpacing);
	m_waterHF->SetParcelOverlapGap(overlapGap);

	// Set water color parcel parameters
	m_waterCF->SetParcelOrigin(CRuVector3(layerOrigin.m_x, 0.0f, layerOrigin.m_z));
	m_waterCF->SetParcelSpacing(gridSpacing);
	m_waterCF->SetParcelOverlapGap(0);

	// Set water flag parcel parameters
	m_waterFlags->SetParcelOrigin(CRuVector3(layerOrigin.m_x, 0.0f, layerOrigin.m_z));
	m_waterFlags->SetParcelSpacing(gridSpacing);
	m_waterFlags->SetParcelOverlapGap(0);

	Mojo_ResetHeight(m_waterHF, layerOrigin.m_y);
	Mojo_ResetFlags(m_waterFlags, 0x00, 0x80);
	Mojo_ResetFlags(m_waterFlags, 0x3F, 0x3F);

	return TRUE;
}

BOOL CRuWorld_Water::PaintHeight(const CRuVector3 &center, float radius, float delta, RuFalloffFunction falloffFunction)
{
	// Perform painting
	Mojo_PaintHeight(m_waterHF, center, radius, delta, 0.0f, FALSE, falloffFunction);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::SetHeight(const CRuVector3 &center, float radius, float height, float brushStrength, RuFalloffFunction falloffFunction)
{
	// Perform painting
	Mojo_SetHeight(m_waterHF, center, radius, height, brushStrength, falloffFunction);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::SmoothHeight(const CRuVector3 &center, float radius, float delta)
{
	// Perform painting
	Mojo_SmoothHeight(m_waterHF, center, radius, delta);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::PaintSurface(const CRuVector3 &center, float radius, BOOL waterVisible)
{
	BYTE waterVisibilityFlag = waterVisible ? 0x80 : 0x00;
	BYTE waterVisibilityMask = 0x80;

	// Perform flag painting
	Mojo_PaintFlags(m_waterFlags, center, radius, waterVisibilityFlag, waterVisibilityMask);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::PaintAlpha(const CRuVector3 &center, REAL radius, REAL delta, RuFalloffFunction falloffFunction, REAL lowerLimit, REAL upperLimit)
{
	// Perform painting
	Mojo_PaintValue(m_waterFlags, center, radius, delta, falloffFunction, 0x3F, lowerLimit, upperLimit);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::PaintColor(const CRuVector3 &center, REAL radius, RUCOLOR color, REAL delta, RuFalloffFunction falloffFunction)
{
	// Perform painting
	Mojo_PaintColor(m_waterCF, center, radius, color, delta, falloffFunction);

	// Rebuild water model
	BuildWaterLayerMesh();

	return TRUE;
}

BOOL CRuWorld_Water::StitchWaterLayer(INT32 neighborID, CRuWorld_Water *neighborLayer)
{
	// Stitch water layer
	m_waterHF->Stitch(neighborID, neighborLayer->m_waterHF);

	return TRUE;
}

BOOL CRuWorld_Water::SetWaterLayerMaterial(IRuMaterial *material)
{
	// Store reference to new material
	material->AddRef();
	ruSAFE_RELEASE(m_waterMaterial);
	m_waterMaterial = material;

	return TRUE;
}

BOOL CRuWorld_Water::BuildCollisionStructure()
{
	if(m_waterMesh->GetCollisionStructure() == NULL)
	{
		// Build collision structure
		CRuCollisionStructure_Quadtree *colStructure = ruNEW CRuCollisionStructure_Quadtree();
		colStructure->BuildCollisionStructure(m_waterMesh, NULL);

		// Set surface flags to conform to that of water surfaces
		CRuCollisionQuery_User_Flag_Set flagSetQuery;
		flagSetQuery.m_markingFlag = 0x00200000;
		flagSetQuery.m_flagMask = 0xFFFFFFFF;
		colStructure->QueryCollision(&flagSetQuery);

		// Set as collision structure for the mesh
		m_waterMesh->SetCollisionStructure(colStructure);

		// Release local copy of collision structure
		ruSAFE_RELEASE(colStructure);
	}

	return TRUE;
}

const CRuGUID &CRuWorld_Water::GetWaterBodyGUID() const
{
	return m_waterBodyGUID;
}

BOOL CRuWorld_Water::ValidateMesh() const
{
	if(m_waterMesh == NULL || m_waterMesh->GetNumPrimitives() == 0)
		return FALSE;

	return TRUE;
}

void CRuWorld_Water::BuildWaterLayerMesh()
{
	// Rebuild the water layer mesh
	ruSAFE_RELEASE(m_waterMesh);
	m_waterMesh = Mojo_BuildMeshFromHeightField(m_waterHF, m_waterCF, m_waterFlags, 0x80);

	this->SetLocalEntityBounds(m_waterMesh->GetBounds());
}

// ************************************************************************************************************************************************************

#pragma managed
