/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "scene3d/mitem_event_script_interface.h"
		#include "scene3d/mitem_scripted_object.h"
		#include "scene3d/mitem_script_unit.h"
		#include "scene3d/mtrigger.h"
		#include "physic/physic_world.h"
		#include "script/script_engine_types.h"
		#include "script/script_variant.h"

		using namespace GS::S3D;
		using namespace GS::Script;


//------------------------------------------------------------------------------
void				MItemScriptEvent::OnSetup(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnSetup"))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnSetupDone(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnSetupDone"))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnReset(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnReset"))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnActivate(MItem *item, bool activate)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->activation_callback && unit->SetupFunctionCall("OnActivate", ((MItemScriptUnit *)unit)->activation_callback))
		{
			unit->PushFunctionCallArgument(activate);
			unit->DoFunctionCall();
		}
}
void				MItemScriptEvent::OnUpdate(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->update_callback && unit->SetupFunctionCall("OnUpdate", ((MItemScriptUnit *)unit)->update_callback))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnPhysicStep(MItem *item, bool step_taken)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->physic_callback && unit->SetupFunctionCall("OnPhysicStep", ((MItemScriptUnit *)unit)->physic_callback))
		{
			unit->PushFunctionCallArgument(step_taken);
			unit->DoFunctionCall();
		}
}
void				MItemScriptEvent::OnCollision(MItem *item, MItem *with, const CollisionPair &pair)
{
	if	(!item->isActive())
		return;

	IVM *vm = item->scripted_object->GetVM();

	// Check if the contact informations will be needed.
	bool contact_info_required = false;

	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->collisionex_callback.IsValid())
		{	contact_info_required = true; break;	}

	if	(contact_info_required)
	{
		AutoPtr <Object> table_object(vm->CreateTable());
		table_object->Set("count", pair.contact_count);

		// Contact point.
		AutoPtr <Object> ctc_object(vm->CreateArray());
		for (uint n = 0; n < pair.contact_count; ++n)
			if	(vm->SetupFunctionCall("Vector"))
			{
				vm->PushArgument(pair.contact[n].x);
				vm->PushArgument(pair.contact[n].y);
				vm->PushArgument(pair.contact[n].z);
				Script::Variant rv;
				vm->DoFunctionCall(&rv);
				ctc_object->Append(rv);
			}

		table_object->Set("p", ctc_object.c_ptr());

		// Contact normal.
		AutoPtr <Object> nrm_object(vm->CreateArray());
		for (uint n = 0; n < pair.contact_count; ++n)
			if	(vm->SetupFunctionCall("Vector"))
			{
				vm->PushArgument(pair.normal[n].x);
				vm->PushArgument(pair.normal[n].y);
				vm->PushArgument(pair.normal[n].z);
				Script::Variant rv;
				vm->DoFunctionCall(&rv);
				nrm_object->Append(rv);
			}

		table_object->Set("n", nrm_object.c_ptr());

		// Call each unit.
		ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
			if	(unit->SetupFunctionCall("OnCollisionEx", ((MItemScriptUnit *)unit)->collisionex_callback))
			{
				unit->PushUserObjectFunctionCallArgument(with, typetag_Item);
				unit->PushFunctionCallArgument(table_object.c_ptr());
				unit->PushFunctionCallArgument(true);
				unit->DoFunctionCall();
			}
	}

	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->collision_callback && unit->SetupFunctionCall("OnCollision", ((MItemScriptUnit *)unit)->collision_callback))
		{
			unit->PushUserObjectFunctionCallArgument((void *)with, typetag_Item);
			unit->DoFunctionCall();
		}
}

void				MItemScriptEvent::OnItemEnter(MTrigger *trigger, MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnItemEnter"))
		{
			unit->PushUserObjectFunctionCallArgument((void *)item, typetag_Item);
			unit->DoFunctionCall();
		}
}
void				MItemScriptEvent::OnItemExit(MTrigger *trigger, MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnItemExit"))
		{
			unit->PushUserObjectFunctionCallArgument((void *)item, typetag_Item);
			unit->DoFunctionCall();
		}
}

void				MItemScriptEvent::OnEnterTrigger(MItem *item, MTrigger *trigger)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnEnterTrigger"))
		{
			unit->PushUserObjectFunctionCallArgument((void *)((MItem *)trigger), typetag_Item);
			unit->DoFunctionCall();
		}
}
void				MItemScriptEvent::OnTrigger(MItem *item, MTrigger *trigger)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnTrigger"))
		{
			unit->PushUserObjectFunctionCallArgument((void *)((MItem *)trigger), typetag_Item);
			unit->DoFunctionCall();
		}
}
void				MItemScriptEvent::OnExitTrigger(MItem *item, MTrigger *trigger)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnExitTrigger"))
		{
			unit->PushUserObjectFunctionCallArgument((void *)((MItem *)trigger), typetag_Item);
			unit->DoFunctionCall();
		}
}

void				MItemScriptEvent::OnRenderDone(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnRenderDone"))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnRenderUser(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(((MItemScriptUnit *)unit)->render_user_callback && unit->SetupFunctionCall("OnRenderUser", ((MItemScriptUnit *)unit)->render_user_callback))
			unit->DoFunctionCall();
}
void				MItemScriptEvent::OnDelete(MItem *item)
{
	ListForeachPtr(Unit *, unit, item->scripted_object->GetUnitList())
		if	(unit->SetupFunctionCall("OnDelete"))
			unit->DoFunctionCall();
}
//------------------------------------------------------------------------------
