//===========================================================================//
// File:	Map.cpp                                                   //
// Project: MUNGA           Brick: Interest Manager	                       //
// Contents: Interface specifications for interest manager						  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  -----------------------------------------------------------//
// 11/04/94  ECH  Initial coding.                                            //
// 11/29/94  JMA  Changed Identities to IDs                                  //
// 02/10/95  CPB  Added GaugeInterestType                                    //
// 08/25/97  ECH  Infrastructure changes.                                    //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"
#include "Map.hpp"
#include "VideoRenderer.hpp"
#include "VideoComponentWeb.hpp"
#include "Application.hpp"
#include "Zone.hpp"
#include <ElementRenderer\GridElement.hpp>
#include "GUITextManager.hpp"
#include <Compost\TerrainTextureLogistic.hpp>
#include <Compost\TexturePool.hpp>
#include "CollisionGrid.hpp"
#include "Tile.hpp"
#include <MLR\MLRCulturShape.hpp>
#include <MLR\MLRFootstep.hpp>
#include <GameOS\ToolOS.hpp>
#include "EntityManager.hpp"

//#define HIERARCHY "your_loginname_here"	// used to display attachment info
#define HUNT_ADD_EFFECT_BUG

//#############################################################################
//###################################    Map    ###############################
//#############################################################################

Map::ClassData*
	Map::DefaultData = NULL;

int Map::LoadRadius = 1;
static bool __stdcall CheckFullZoneLoad() {return Map::LoadRadius==4;}
static void __stdcall EnableFullZoneLoad() {Map::LoadRadius = 5-Map::LoadRadius;}

DECLARE_TIMER(static, Attaching_To_Map);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::InitializeClass(Stuff::NotationFile *startup_ini)
{
	Verify(!DefaultData);
	Check_Object(ExecutionStateEngine::DefaultData);
	DefaultData =
		new ClassData(
			MapClassID,
			"Adept::Map",
			Entity::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			NULL,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel
		);
	Check_Object(DefaultData);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Map__GameModel,
		DamageCraterResource,
		m_damageCraterResourceID,
		ResourceID
	);

	Initialize_Timer(Attaching_To_Map, "Attaching To Map");
	AddDebuggerMenuItem("Libraries\\Adept\\Load All Zones", CheckFullZoneLoad, EnableFullZoneLoad, 0 );

	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->FindPage("Graphics Options");
		if (page)
		{
			Check_Object(page);
			page->GetEntry("LoadRadius", &LoadRadius);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::TerminateClass(Stuff::NotationFile *startup_ini)
{
	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->SetPage("Graphics Options");
		Check_Object(page);
		page->SetEntry("LoadRadius", LoadRadius);
	}

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

#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Map*
	Map::Make(
		const CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);

	//
	//-----------------------------------------------------------------------
	// We need to open up the game model specified by the message in order to
	// find the ResourceID of our grid element
	//-----------------------------------------------------------------------
	//
	Resource model_resource;
	GetGameModelResourceFromDataListID(&model_resource, message->dataListID);
	GameModel *model = Cast_Pointer(GameModel*, model_resource.GetPointer());
	Check_Object(model);
	Resource grid_resource(model->m_gridElementResourceID);
	Check_Object(&grid_resource);
	Verify(grid_resource.DoesResourceExist());

	ElementRenderer::GridElement *grid =
		Cast_Object(
			ElementRenderer::GridElement*,
			ElementRenderer::Element::Create(
				&grid_resource,
				ElementRenderer::ReadERFVersion(&grid_resource)
			)
		);
	Map *new_entity = new Map(DefaultData, message, base_id, grid);
	Check_Object(new_entity);

	return new_entity;
}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	Map::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	//
	//---------------------------------------------------------------------
	// Make sure there is enough room for the factory request on the stream
	//---------------------------------------------------------------------
	//
	stream->AllocateBytes(sizeof(CreateMessage));
	Entity::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	//
	//----------------------------------------
	// Create the memory stream for the props
	//----------------------------------------
	//
	DynamicMemoryStream prop_stream(sizeof(WORD));
	ReplicatorID span = ReplicatorID::Null;
	prop_stream << span.localID;

	//
	//--------------------------------------------------------------------
	// Tell each prop to save itself and bump the stream pointer afterword
	//--------------------------------------------------------------------
	//
	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;
	while ((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		if (entity->GetPropType() == MapPropType)
		{
			Replicator::CreateMessage *create_message =
				entity->SaveMakeMessage(&prop_stream, NULL);
			if (create_message)
			{
				Check_Object(create_message);
				prop_stream.AdvancePointer(create_message->messageLength);
				span += create_message->replicatorID.localID;
			}
		}
	}

	//
	//-----------------------------------------
	// Only save out the stream if there is one
	//-----------------------------------------
	//
	if (prop_stream.GetBytesUsed() > sizeof(WORD))
	{
		prop_stream.Rewind();
		Verify(span.localID > 0);
		prop_stream << span.localID;
		Resource prop_resource(NULL);
		prop_resource.Save(&prop_stream, NULL);
		STOP(("Not updated"));
		Check_Object(&prop_resource);
		message->m_propStreamResourceID = prop_resource.GetResourceID();
		message->replicatorID += span;
	}
	else
		message->m_propStreamResourceID = ResourceID::Null;
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Map::Map(
	ClassData *class_data,
	const CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::GridElement *element,
	int execution_slots
):
	Entity(class_data, message, base_id, element),
	m_executingZones(NULL),
	m_renderedZones(NULL),
	m_renderOrigin(NULL),
	m_executionSlots(execution_slots)
{
	Check_Pointer(this);
	Check_Object(message);

	//
	//---------------------------------------------------------------------
	// We will get our size directly from the .erf file that has the grid
	// element in it.  In the mean time, set up the structures for one zone
	//---------------------------------------------------------------------
	//
	ElementRenderer::GridElement *grid =
		Cast_Object(ElementRenderer::GridElement*, entityElement);
	grid->GetSize(&m_rowZoneCount, &m_columnZoneCount);
	m_zoneCount = static_cast<WORD>(m_columnZoneCount * m_rowZoneCount);

	grid->SetSize(m_zoneCount);

	Scalar row_size, column_size;
	grid->GetDimensions(
		&m_rowZoneOrigin,
		&m_columnZoneOrigin,
		&row_size,
		&column_size
	);
	Verify(!Small_Enough(row_size));
	m_rowZoneScale = 1.0f / row_size;
	Verify(!Small_Enough(column_size));
	m_columnZoneScale = 1.0f / column_size;
	Verify(Close_Enough(m_columnZoneScale, m_rowZoneScale));

	//
	//--------------------------------------------------
	// Set up the game model and set up the list of erfs
	//--------------------------------------------------
	//
	const GameModel *model = GetGameModel();
	Check_Object(model);
	m_zonesResource.FindID(model->m_zoneArrayResourceID);
	Verify(m_zonesResource.DoesResourceExist());
	Verify(m_zoneCount*sizeof(Zone::ResourceData) == m_zonesResource.GetSize());

	//
	//---------------------
	// Create the supergrid
	//---------------------
	//
	Verify(!CollisionGrid::Instance);
	gos_PushCurrentHeap(s_CollisionHeap);
	CollisionGrid::Instance =
		new CollisionGrid(
			static_cast<BYTE>(m_rowZoneCount*e_TilesPerZone),
			static_cast<BYTE>(m_columnZoneCount*e_TilesPerZone),
			m_rowZoneOrigin,
			m_columnZoneOrigin,
			row_size * m_rowZoneCount,
			column_size * m_columnZoneCount,
			model->m_waterLevel
		);
	Check_Object(CollisionGrid::Instance);
	gos_PopCurrentHeap();

	//
	//------------------------------------
	// Build the Texture logistics manager
	//------------------------------------
	//
	bool manage_textures = model->m_textureDataResourceID != ResourceID::Null;
	bool dedicated_server = Application::GetInstance()->m_localMissionParameters->m_runDedicated!=0 ? true : false;
	if (manage_textures && Compost::TerrainTextureLogistic::GetResolution()>0 && !dedicated_server)
	{
		gos_PushCurrentHeap(Compost::Heap);
		Compost::TexturePool::Instance = new Compost::TexturePool;
		Check_Object(Compost::TexturePool::Instance);
		//
		//-----------------------------------------------------------
		// Set up the texture path until texture data stream is coded
		//-----------------------------------------------------------
		//
		char name[1024];
		gos_GetCurrentPath(name, 1024);
		Compost::TexturePool::Instance->SetTexturePath("Content\\Textures\\CompostTexture");

		Verify(model->m_tcfResourceID!=ResourceID::Null);
		Resource tcf_resource(model->m_tcfResourceID);

		NotationFile tcf_data(&tcf_resource);

		Compost::TexturePool::Instance->LoadIndex(&tcf_data);

		Resource texture_spec(model->m_textureDataResourceID);
		Verify(texture_spec.DoesResourceExist());
		Compost::TerrainTextureLogistic::Instance =
			new Compost::TerrainTextureLogistic(
				m_columnZoneCount*e_TilesPerZone,
				m_rowZoneCount*e_TilesPerZone,
				c_TileSize,
				c_TileSize,
				&texture_spec
			);
		Check_Object(Compost::TerrainTextureLogistic::Instance);

		Compost::TerrainTextureLogistic::Instance->SetOffset(
			m_rowZoneOrigin,
			m_columnZoneOrigin
			);
		gos_PopCurrentHeap();
	}

	//
	//----------------------------
	// Allocate the array of Zones
	//----------------------------
	//
	Verify(m_zoneCount > 0 && m_zoneCount == m_columnZoneCount * m_rowZoneCount);
	m_zones = new Zone[m_zoneCount];
	Check_Pointer(m_zones);
	Zone *zone=m_zones;
	WORD index=0;
	for (BYTE row=0; row<m_rowZoneCount; ++row)
	{
		for (BYTE column=0; column<m_columnZoneCount; ++column, ++index, ++zone)
		{
			//
			//----------------------------------------
			// Load the erf for the zone and attach it
			//----------------------------------------
			//
			Check_Object(zone);
			zone->m_row = row;
			zone->m_column = column;
			zone->LockBounds();
			zone->CreateTileGrid(
				m_rowZoneOrigin + row*c_ZoneSize,
				m_columnZoneOrigin + column*c_ZoneSize
			);
			grid->AttachIndexedChild(row, column, zone);
		}
	}

	//
	//-----------------
	// Create the props
	//-----------------
	//
	propListResourceID = ResourceID::Null;

	if (message->m_propStreamResourceID != ResourceID::Null)
	{
		Resource stream(message->m_propStreamResourceID);
		Verify(stream.DoesResourceExist());
		stream.AdvancePointer(replicatorID.localID);
		while (stream.GetBytesRemaining() > 0)
		{
			Entity::CreateMessage *message =
				Cast_Pointer(Entity::CreateMessage*, stream.GetPointer());
			Check_Object(message);
			Entity *entity = CreateEntity(&stream, base_id);
			Check_Object(entity);
			entity->SetPropType(MapPropType);
			AddChild(entity);
			stream.AdvancePointer(message->messageLength);
		}
		propListResourceID = message->m_propStreamResourceID;
	}

	//
	//------------------------
	// Build the cultural data
	//------------------------
	//
	bool manage_culturals = model->m_culturalDataResourceID != ResourceID::Null;
	if (manage_culturals)	// switch to turn off cuturals
	{
		Resource culturalData_resource(model->m_culturalDataResourceID);

		int mlr_version = MidLevelRenderer::ReadMLRVersion(&culturalData_resource);

		OBB obb;
		culturalData_resource >> obb;

		if(mlr_version>13)
		{
			RegisteredClass::ClassID class_id;
			culturalData_resource >> class_id;
		}

		MidLevelRenderer::MLRCulturShape::culturalRevolution = MidLevelRenderer::MLRShape::Make(&culturalData_resource, mlr_version);
	}
	else
	{
		if(MidLevelRenderer::MLRCulturShape::culturalRevolution!=NULL)
		{
			MidLevelRenderer::MLRCulturShape::culturalRevolution->DetachReference();

			MidLevelRenderer::MLRCulturShape::culturalRevolution = NULL;
		}
	}

	//
	//------------------------
	//Set up the footstep pool
	//------------------------
	//
	Verify(!MidLevelRenderer::MLRFootStep::Instance);
	MidLevelRenderer::MLRFootStep::Instance = new MidLevelRenderer::MLRFootStep(64);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Map::~Map()
{
	Check_Object(this);

	GlobalPointers::ClearPointer(MapGlobalPointerIndex);

	//
	//----------------------------------------------
	// Tell all the children to become uninteresting
	//----------------------------------------------
	//
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;
	while ((entity = children.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->BecomeUninteresting();
	}

	//
	//---------------------------------------------
	// Turn off the terrain compositing controllers
	//---------------------------------------------
	//
	if (Compost::TerrainTextureLogistic::Instance)
	{
		Check_Object(Compost::TerrainTextureLogistic::Instance);
		delete Compost::TerrainTextureLogistic::Instance;
		Compost::TerrainTextureLogistic::Instance = NULL;
	}
	if (Compost::TexturePool::Instance)
	{
		Check_Object(Compost::TexturePool::Instance);
		delete Compost::TexturePool::Instance;
		Compost::TexturePool::Instance = NULL;
	}

	//
	//-----------------
	// Delete our zones
	//-----------------
	//
	Check_Pointer(m_zones);
	delete[] m_zones;

	//
	//-------------------------------------------------------------------
	// Delete our collision grid - our parent class will take care of our
	// child entities
	//-------------------------------------------------------------------
	//
	Check_Object(CollisionGrid::Instance);
	delete CollisionGrid::Instance;
	CollisionGrid::Instance = NULL;

	//
	//----------------------
	//Kill the FootStep pool
	//----------------------
	//
	if (MidLevelRenderer::MLRFootStep::Instance)
	{
		delete MidLevelRenderer::MLRFootStep::Instance;
		MidLevelRenderer::MLRFootStep::Instance = NULL;
	}

	//
	//------------------------
	//Kill the cultural meshes
	//------------------------
	//
	if(MidLevelRenderer::MLRCulturShape::culturalRevolution!=NULL)
	{
		MidLevelRenderer::MLRCulturShape::culturalRevolution->DetachReference();

		MidLevelRenderer::MLRCulturShape::culturalRevolution = NULL;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::AddChild(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	//
	//------------------------------------------
	// Find the zone that this entity belongs to
	//------------------------------------------
	//
	Start_Timer(Attaching_To_Map);
	ElementRenderer::Element *child_element = entity->GetElement();
	Check_Object(child_element);

#ifdef HUNT_ADD_EFFECT_BUG

	if (entity == NULL)
	{
		STOP(("ADDING OBJECT FAILED : ENTITY NULL"));
	}
	if (child_element == NULL)
	{
		STOP(("ADDING OBJECT FAILED : CHILD ELEMENT NULL"));
	}

#endif


	Point3D translation(child_element->GetNewLocalToParent());
	int index = FindZone(translation);
	Verify(static_cast<unsigned>(index) < m_zoneCount);
	Zone *zone = &m_zones[index];
	Check_Object(zone);


#ifdef HUNT_ADD_EFFECT_BUG

	if (zone == NULL)
	{
		STOP(("ADDING OBJECT FAILED : location : %f %f %f  Zone : %d", translation.x, translation.y, translation.z, index));
	}

	if (static_cast<unsigned>(index) >= m_zoneCount)
	{
		STOP(("ADDING OBJECT FAILED : location : %f %f %f  Zone : %d", translation.x, translation.y, translation.z, index));
	}

#endif


	//
	//-----------------------------
	// Hook up the entity hierarchy
	//-----------------------------
	//
	Verify(!entity->parentEntity);
	entity->parentEntity = this;
	childEntityChain.Add(entity);
	entity->NeedMatrixSync();
	#if defined(HIERARCHY)
		SPEW((0, "Attaching %s(+", entity->GetClassString()));
		Spew(0, entity->GetReplicatorID());
		SPEW((0, ") to %s(+", GetClassString()));
		Spew(0, GetReplicatorID());
		SPEW((0, ")"));
	#endif
	zone->AttachChild(child_element);
	Stop_Timer(Attaching_To_Map);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::RemoveChild(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	//
	//--------------------------------
	// Unhook the entity from the zone
	//--------------------------------
	//
	ElementRenderer::Element *element = entity->GetElement();
	Check_Object(element);
	
	ElementRenderer::Element *parent_ele=element->GetParentElement();
	Check_Object(parent_ele);

	Zone *zone;
	if(parent_ele->GetClassID() == TileClassID)
	{
		Verify(parent_ele->IsDerivedFrom(Tile::DefaultData));
		zone = Cast_Object(Zone*, parent_ele->GetParentElement()->GetParentElement());
	}
	else
	{
		zone = Cast_Object(Zone*, parent_ele);
	}

	zone->DetachChild(element);

	//
	//---------------------------------
	// Unook the entity from the parent
	//---------------------------------
	//
	entity->parentEntity = NULL;
	childEntityChain.Remove(entity);
	entity->NeedMatrixSync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::ChildPreCollisionChanged(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	//
	//-----------------------------
	// Find the zone for the entity
	//-----------------------------
	//
	ElementRenderer::Element *element = entity->GetElement();
	Check_Object(element);
	
	ElementRenderer::Element *parent_ele=element->GetParentElement();
	Check_Object(parent_ele);

	Zone *zone;
	if(parent_ele->GetClassID() == TileClassID)
	{
		Verify(parent_ele->IsDerivedFrom(Tile::DefaultData));
		zone = Cast_Object(Zone*, parent_ele->GetParentElement()->GetParentElement());
	}
	else
	{
		zone = Cast_Object(Zone*, parent_ele);
	}

	//
	//-------------------
	// Turn the entity on
	//-------------------
	//
	if((entity->executionState->GetOldState() == ExecutionStateEngine::NeverExecuteState)
		&&(entity->executionState->GetState() != ExecutionStateEngine::NeverExecuteState))
	{
		zone->AddExecutor(entity);
	}

	//
	//--------------------
	// Turn off the entity
	//--------------------
	//
	else if((entity->executionState->GetOldState() != ExecutionStateEngine::NeverExecuteState)
		&&(entity->executionState->GetState() == ExecutionStateEngine::NeverExecuteState))
	{
		zone->RemoveExecutor(entity);
	}
}

#if defined(NDEBUG)
	#pragma optimize("a",off)
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::UpdateZone(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);
	Verify(entity->parentEntity == this);
	Verify(this == Map::GetInstance());
	Verify(!entity->IsTileBound());

	//
	//------------------------------------------
	// Find out where the child wants to move to
	//------------------------------------------
	//
	ElementRenderer::Element *child_element = entity->GetElement();
	Check_Object(child_element);
	Zone *old_zone = Cast_Object(Zone*, child_element->GetParentElement());
	Point3D translation(entity->GetLocalToWorld());

	//
	//------------------
	// Find the new zone
	//------------------
	//
	Scalar column = translation.x - m_columnZoneOrigin;
	Scalar row = translation.z - m_rowZoneOrigin;
	column *= m_columnZoneScale;
	row *= m_rowZoneScale;
	Verify(column>=0.0f && column<m_columnZoneCount && row>=0.0f && row<m_rowZoneCount);

	//
	//--------------------------------------------------------------------------
	// See if the child is moving to a new zone.  If so, change the zones of the
	// child.  We are manipulating the entity's element flags directly rather
	// than calling a sync because we are only changing parentage, and both are
	// rooted at the origin.
	//--------------------------------------------------------------------------
	//
	int index = Truncate_Float_To_Byte(row)*m_columnZoneCount + Truncate_Float_To_Byte(column);
	Verify(static_cast<unsigned>(index) < m_zoneCount);
	Zone *new_zone = &m_zones[index];
	Check_Object(new_zone);
	if (old_zone != new_zone || entity->GetCollisionMask() == EditorIsMovingFlag)
	{
		AbstractEvent *event = entity->m_pendingInterestMessage.GetCurrent();
		if (event)
			event->Process();
		InterestLevel level = entity->GetInterestLevel();
		unsigned element_state = child_element->GetElementState();
		old_zone->DetachChild(child_element);
		new_zone->AttachChild(child_element);
		if (level == entity->GetInterestLevel())
			child_element->SetElementState(element_state);
	}

	//
	//------------------------------
	// Clear the entity's dirty flag
	//------------------------------
	//
	entity->replicatorFlags &= ~DirtyZoneFlag;
}
#if defined(NDEBUG)
	#pragma optimize("a",on)
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Map::FindZone(const Stuff::Point3D &where)
{
	Check_Object(this);
	Verify(
		Close_Enough(
			GetElement()->GetLocalToWorld(),
			LinearMatrix4D::Identity
		)
	);
	Verify(
		Close_Enough(
			GetElement()->GetNewLocalToParent(),
			LinearMatrix4D::Identity
		)
	);
	unsigned index = GetElement()->FindElementIndex(where.z, where.x);
	if (index < GetElement()->GetActiveCount())
		return index;
	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::GetMapExtents(
		Stuff::Scalar *min_z,
		Stuff::Scalar *max_z,
		Stuff::Scalar *min_x,
		Stuff::Scalar *max_x
	)
{
	Check_Object(this);
	Check_Pointer(min_z);
	Check_Pointer(max_z);
	Check_Pointer(min_x);
	Check_Pointer(max_x);

	*min_x = m_columnZoneOrigin;
	*max_x = m_columnZoneOrigin + m_columnZoneCount/m_columnZoneScale;
	*min_z = m_rowZoneOrigin;
	*max_z = m_rowZoneOrigin + m_rowZoneCount/m_rowZoneScale;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Map");

	//
	//---------------------------------
	// Verify that this always executes
	//---------------------------------
	//
	Check_Object(executionState);
	Verify(
		executionState->GetState() != 
		ExecutionStateEngine::NeverExecuteState
	);
	Entity::PreCollisionExecute(till);

	//
	//-------------------------------
	// Clear out the execution hashes
	//-------------------------------
	//
	int i;
	for (i=0; i<m_executionSlots.GetLength(); ++i)
		m_executionSlots[i] = NULL;

	//
	//--------------
	// Execute zones
	//--------------
	//
	ChainIteratorOf<Zone*> zones(&m_executingZones);
	Zone *zone;
	while ((zone = zones.ReadAndNext()) != NULL)
	{
		Check_Object(zone);
		Verify(zone->GetInterestLevel() != DormantInterestLevel);
		zone->PreCollisionExecute(till);
	}

	//
	//-------------------------
	// Now execute the entities
	//-------------------------
	//
	for (i=0; i<m_executionSlots.GetLength(); ++i)
	{
		Entity *entity = m_executionSlots[i];
		while (entity)
		{
			Check_Object(entity);
			entity->PreCollisionExecute(till);
			Verify(!entity->IsUsingPostCollision() || EntityManager::GetInstance()->IsInPostCollisionExecution(entity));
			entity = entity->nextPreExecution;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::SyncMatrices(bool update_matrix)
{
	Check_Object(this);
	SYNC_LOGIC("Map");

	#if defined(HIERARCHY)
		if (update_matrix)
		{
			SPEW((HIERARCHY, "Map::SyncMatrices %s(+", GetClassString()));
			Spew(HIERARCHY, GetReplicatorID());
			SPEW((HIERARCHY, ")"));
		}
	#endif

	//
	//-----------------------------------------
	// If we have a dirty locator, sync with it
	//-----------------------------------------
	//
	if (IsMatrixDirty())
	{
		if (update_matrix)
		{
			Check_Object(entityElement);
			entityElement->Sync();
			update_matrix = false;
		}
		ClearNeedMatrixSync();
	}

	//
	//-------------------------
	// Sync the executing zones
	//-------------------------
	//
	ChainIteratorOf<Zone*> zones(&m_executingZones);
	Zone *zone;
	while ((zone = zones.ReadAndNext()) != NULL)
	{
		Check_Object(zone);
		Verify(zone->GetInterestLevel() != DormantInterestLevel);
		zone->SyncMatrices(update_matrix);
	}

	#if defined(HIERARCHY)
		if (update_matrix)
			SPEW((0, ""));
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::UpdateRenderOrigin(const Stuff::Point3D &location)
{
	Check_Object(this);
	Check_Object(&location);

	//
	//------------------------------------------------------------------
	// Find the zone that this entity belongs to, and if the zone hasn't
	// changed, we are done
	//------------------------------------------------------------------
	//
	int index = FindZone(location);
	Verify(static_cast<unsigned>(index) < m_zoneCount);
	Zone *new_zone = &m_zones[index];
	Check_Object(new_zone);
	if (new_zone == m_renderOrigin)
		return;

	//
	//--------------------------------------------
	// Find the bounds on where the render will be
	//--------------------------------------------
	//
	unsigned row = index / m_columnZoneCount;
	unsigned column = index - row*m_columnZoneCount;
	unsigned min_column = (column>=LoadRadius) ? column-LoadRadius : 0;
	unsigned min_row = (row>=LoadRadius) ? row-LoadRadius : 0;
	unsigned max_column = (column+LoadRadius >= m_columnZoneCount) ? m_columnZoneCount : column+LoadRadius+1;
	unsigned max_row = (row+LoadRadius >= m_rowZoneCount) ? m_rowZoneCount : row+LoadRadius+1;

	//
	//----------------------------------------------------------
	// Make any active zone not in the active zone uninteresting
	//----------------------------------------------------------
	//
	ChainIteratorOf<Zone*> zones(&m_renderedZones);
	Zone *zone;
	while ((zone = zones.ReadAndNext()) != NULL)
	{
		Check_Object(zone);
		if (
			zone->m_row < min_row || zone->m_row >= max_row
			 || zone->m_column < min_column || zone->m_column >= max_column
		)
			zone->DisableRendering();
	}

	//
	//-------------------------------------
	// Tell each zone to become interesting
	//-------------------------------------
	//
	m_renderOrigin = new_zone;
	for (row=min_row; row<max_row; ++row)
	{
		zone = &m_zones[row*m_columnZoneCount + min_column];
		for (column=min_column; column<max_column; ++column,++zone)
		{
			if (zone->GetInterestLevel() != RenderingInterestLevel)
				zone->EnableRendering();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::ActivateZones()
{
	Check_Object(this);

	//
	//-------------------------------------
	// Tell each zone to become interesting
	//-------------------------------------
	//
	for (int row=0; row<m_rowZoneCount; ++row)
	{
		for (int column=0; column<m_columnZoneCount; ++column)
		{
			m_zones[row*m_columnZoneCount + column].ActivateZone();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Zone::ResourceData*
	Map::GetZoneResourceData(unsigned index)
{
	Check_Object(this);
	return &Cast_Pointer(Zone::ResourceData*, m_zonesResource.GetPointer())[index];
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map::WorldToZoneCoords(Stuff::Scalar& x, Stuff::Scalar& y) const
{
	x = (x - m_columnZoneOrigin) * m_columnZoneScale;
	y = (y - m_rowZoneOrigin) * m_rowZoneScale; // jcem - m_columnZoneScale->m_rowZoneScale
}
