//===========================================================================//
// File:	DropZone.cpp																	     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/08/99  DPB  Created file                          //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "DropZone.hpp"
#include "Driver.hpp"
#include "EntityManager.hpp"
#include "CollisionGrid.hpp"
#include "Application.hpp"

//#############################################################################
//###############################    DropZone      ############################
//#############################################################################

DropZone::ClassData*
	DropZone::DefaultData = NULL;



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DropZone::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			DropZoneClassID,
			"Adept::DropZone",
			BaseClass::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
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DropZone::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DropZone*
	DropZone::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	DropZone *new_entity = new DropZone(DefaultData, message, base_id, NULL);
	Check_Object(new_entity);
	Check_Object(EntityManager::GetInstance());
	return new_entity;
}

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

	stream->AllocateBytes(sizeof(CreateMessage));
	BaseClass::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	message->dropPointsResourceID = dropPointsResourceID;

	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DropZone::DropZone(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Entity(class_data, message, base_id, element),
	dropMats(NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	chainSize = 0;
	//
	//----------------------------------
	//We need to load in the zone points
	//----------------------------------
	//
	dropPointsResourceID = message->dropPointsResourceID;

	if(message->dropPointsResourceID != ResourceID::Null)
	{
		Resource stream(message->dropPointsResourceID);
		Verify(stream.DoesResourceExist());
		int num_points;
		stream >> num_points;
		for(int i=0; i<num_points; i++)
		{
			LinearMatrix4D point;
			stream >> point;
			MatrixPlug *point_plug = new MatrixPlug(point);
			dropMats.Add(point_plug);
			origDropMats.Add(point_plug);
		}
	}


	if (Application::GetInstance()->networkingFlag)
	{

		

		chainSize = dropMats.GetSize();

		int rotate = Random::GetLessThan(chainSize-1);

		for (int i = 0; i < rotate; ++i)
		{
			ChainIteratorOf<MatrixPlug *> iterator(&dropMats);
			MatrixPlug *point_plug = iterator.GetCurrent();
			dropMats.Remove(point_plug);
			dropMats.Add(point_plug);
		}

		int mix = Random::GetLessThan(32);
		if (chainSize > 0)
		{
			for (int i = 0; i < mix; ++i)
			{

				int drop_remove = Random::GetLessThan(chainSize-1);
				
				ChainIteratorOf<MatrixPlug *> iterator(&dropMats);

				for (int i = 0; i < drop_remove; ++i)
				{
					iterator.Next();
				}

				MatrixPlug *point_plug = iterator.GetCurrent();
				dropMats.Remove(point_plug);
				dropMats.Add(point_plug);

			}
		}
	}


}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DropZone::~DropZone()
{
	dropMats.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::LinearMatrix4D DropZone::RequestDrop(void)
{
	Check_Object(this);
	
	SyncMatrices(true);


	bool found_drop = false;

	int loop_break = 0;

	Point3D new_translation = Point3D::Identity;
	YawPitchRoll new_rotation = YawPitchRoll::Identity;
	LinearMatrix4D drop_location = LinearMatrix4D::Identity;

	if (Application::GetInstance()->IsCampCOOP())
	{ // jcem - no random for single play coin version...
		new_rotation = GetLocalToWorld();
		new_translation = GetLocalToWorld();
		found_drop = true;
	}

	while (!found_drop)
	{	
		if(!dropMats.IsEmpty())
		{
			ChainIteratorOf<MatrixPlug *> iterator(&dropMats);
			MatrixPlug *point_plug = iterator.GetCurrent();
			dropMats.Remove(point_plug);
			dropMats.Add(point_plug);
			drop_location = point_plug->GetItem();
			new_rotation = (YawPitchRoll) drop_location;
			new_translation = (Point3D) drop_location;

			found_drop = Application::GetInstance()->CheckValidDrop(new_translation);

			loop_break++;

			if (loop_break > chainSize)
				found_drop = true;
		}
		else
		{
			new_rotation = GetLocalToWorld();
			new_translation = GetLocalToWorld();
			found_drop = true;
		}
	}

	drop_location.BuildTranslation(new_translation);
	drop_location.BuildRotation(new_rotation);

	return drop_location;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DropZone::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	DropZone::IsWithin(
		Entity *target,
		Scalar distance,
		bool ignorey
	)
{
	Check_Object(this);
	Check_Object(target);

	if (Entity::IsWithin(target,distance,ignorey) == true)
	{
		return (true);
	}

	Point3D target_position(target->GetLocalToWorld());
	distance *= distance;

	Stuff::ChainIteratorOf<MatrixPlug*> i(&dropMats);
	MatrixPlug* matrix = 0;

	while ((matrix = i.ReadAndNext()) != 0)
	{
		if (matrix->GetPointer() != 0)
		{
			Stuff::LinearMatrix4D* p_matrix = matrix->GetPointer();
			Stuff::Point3D drop_point(*p_matrix);

			if (ignorey == true)
			{
				drop_point.y = target_position.y;
			}

			Stuff::Vector3D delta;
			delta.Subtract(drop_point,target_position);

			if (delta.GetLengthSquared() < distance)
			{
				return (true);
			}
		}
	}

	return (false);
}
