/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**
 * implementation of cInteractiveObject
 */

#include "cInteractiveObject.hh"
#include "events_cWorld.hh"
#include "events_cInteractiveObject.hh"

//#define DEBUG_RECEIVE 1
//#define DEBUG_SUBSCRIBE 1

#ifndef DEBUG_RECEIVE
#define DEBUG_RECEIVE 0
#endif
#ifndef DEBUG_SUBSCRIBE
#define DEBUG_SUBSCRIBE 0
#endif

const string ccIO_SelectObject = string("SelectObject");
const string ccIO_UnSelectObject = string("UnSelectObject");
const string ccIO_RotateObject = string("RotateObject");
const string ccIO_RotateObjectGlobal = string("RotateObjectGlobal");
const string ccIO_MoveObject = string("MoveObject");
const string ccIO_MoveObjectGlobal = string("MoveObjectGlobal");
const string ccIO_ShowObject = string("ShowObject");
const string ccIO_HideObject = string("HideObject");
const string ccIO_ToggleVisibility = string("ToggleVisibility");
const string ccIO_SetObjectMaterial = string("SetObjectMaterial");

/**
 * A class for a visible object which is able to react on several
 * events.
 */
cInteractiveObject::cInteractiveObject (
	cEventDispatcher *disp, 
	const char *name)
:	cVisibleObject (name),
	cEventConsumer (disp),
	mSelected (false)
{
}

/**
 * constructor with initialization
 	* @param disp cEventDispatcher to register/unregister at
	* @param pos initial position of object
	* @param name optional name of object (defaults to class name)
	*/
cInteractiveObject::cInteractiveObject (
	cEventDispatcher *disp, 
	const cVertex &pos, 
	const char *name)
:	cVisibleObject (pos, name),
	cEventConsumer (disp),
	mSelected (false)
{
}

/**
 * constructor with initialization
 	* @param disp cEventDispatcher to register/unregister at
	* @param pos initial position of object
	* @param rot initial rotation of object
	* @param name optional name of object (defaults to class name)
	*/
cInteractiveObject::cInteractiveObject (
	cEventDispatcher *disp, 
	const cVertex &pos, 
	const cQuaternion &rot,
	const char *name)
:	cVisibleObject (pos, rot, name),
	cEventConsumer (disp),
	mSelected (false)
{
}

/**
 * copy constructor
 	*/
cInteractiveObject::cInteractiveObject (const cInteractiveObject &cio)
:	cVisibleObject (cio),
	cEventConsumer (cio),
	mSelected (false)	// only one object selected at a time!
{
}

/**
 * destructor (unregistering done by ~cEventConsumer)
 	*/
cInteractiveObject::~cInteractiveObject ()
{
}

/**
 * initialization
 	*/
int cInteractiveObject::Init ()
{
	SubscribeToPassiveEvents ();
	return cVisibleObject::Init ();
}

/**
 * event receiver
 	*/
int cInteractiveObject::ReceiveEvent (const cEvent &event)
{
	const string &en = event.GetName ();
	// ease casting to different event types
	const cEvent *ev = &event;

#if DEBUG_RECEIVE
	cerr << GetName () << ": received event " << en << endl;
#endif
	// now figure out type of event
	if (en == ccIO_SelectObject)
	{

		if (((cAddressEvent *)ev)->GetAddress() == (void *)this)
		{
			// are we currently active?
			if (mSelected == true)
			{
				// we need to unsubscribe from any event only
				// the active object might receive
				UnsubscribeFromActiveEvents ();
				mSelected = false;
			}
			else
			{
				// send deselection (only received by currently active
				// object, if any
				mDispatcher->SendEvent (cEvent (ccIO_UnSelectObject));

				// subscribe to all events of interest for
				// an active object
				SubscribeToActiveEvents ();
				mSelected = true;
			}

#if DEBUG_RECEIVE
			cerr << GetName() << ": accepted " << en << endl;
#endif

			return 0;
		}
		else	// the event was not for us
		{
#if DEBUG_RECEIVE
			cerr << GetName() << ": ignored " << en << endl;
#endif

			return -1;
		}

	}

	// explicit deselection
	if (en == ccIO_UnSelectObject)
	{
#if DEBUG_RECEIVE
		cerr << GetName() << ": accepted " << en << endl;
#endif

		mSelected = false;
		UnsubscribeFromActiveEvents ();
		return 0;
	}

	if (en == ccIO_RotateObject)
	{
		const cRotationEvent *rev = (cRotationEvent *)ev;

		RotateBy (rev->GetAngle(), rev->GetAxis ());
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_RotateObjectGlobal)
	{
		const cRotationEvent *rev = (cRotationEvent *)ev;
		cVertex r_axis = rev->GetAxis ();
		GLfloat r_angle = rev->GetAngle ();

		cVertex g_axis = mGlobalTransformation/r_axis;

		RotateBy (r_angle, g_axis);
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_MoveObject)
	{
		MoveBy (((cVertexEvent *)ev)->GetVertex());
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_MoveObjectGlobal)
	{
		MoveBy (mGlobalTransformation/(((cVertexEvent *)ev)->GetVertex()));
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_SetObjectMaterial)
	{
		return -1;
	}

	if (en == ccIO_ShowObject)
	{
		SetVisible (true);
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_HideObject)
	{
		SetVisible (false);
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	if (en == ccIO_ToggleVisibility)
	{
		SetVisible (IsVisible() == false);
		mDispatcher->SendEvent (cEvent (ccW_DoRedisplay));
		return 0;
	}

	return -1;

}

/**
 * subscribe to any events we want to receive as the active object
 	*/
void cInteractiveObject::SubscribeToActiveEvents ()
{
	// we need to subscribe to several actions now
	mDispatcher->SubscribeToEvent (
		cEvent (ccIO_UnSelectObject),
		this);
	mDispatcher->SubscribeToEvent (
		cVertexEvent (ccIO_MoveObject),
		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cVertexEvent (ccIO_MoveObjectGlobal),
		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cRotationEvent (ccIO_RotateObject),
		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cRotationEvent (ccIO_RotateObjectGlobal),
		(cEventConsumer *)this);
//	mDispatcher->SubscribeToEvent (
//		cMaterialEvent (ccIO_SetObjectMaterial),
//		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cEvent (ccIO_ShowObject),
		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cEvent (ccIO_HideObject),
		(cEventConsumer *)this);
	mDispatcher->SubscribeToEvent (
		cEvent (ccIO_ToggleVisibility),
		(cEventConsumer *)this);
#if DEBUG_SUBSCRIBE
	cerr << GetName () << ": subscribed to active events " << endl;
	mDispatcher->DisplayRegistered ();
#endif
}

/**
 * subscribe to "passive" events
 	*/
void cInteractiveObject::SubscribeToPassiveEvents ()
{
	// we need to have a chance to get activated...
	mDispatcher->SubscribeToEvent (
		cAddressEvent (ccIO_SelectObject, (void *)this),
		(cEventConsumer *)this);
}


/**
 * unsubscribe from any events we are not anymore interested in
 * (since we're not active anymore)
 */
void cInteractiveObject::UnsubscribeFromActiveEvents ()
{
	mDispatcher->UnsubscribeFromEvent (
		cEvent (ccIO_UnSelectObject),
		this);
	mDispatcher->UnsubscribeFromEvent (
		cVertexEvent (ccIO_MoveObject),
		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cVertexEvent (ccIO_MoveObjectGlobal),
		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cRotationEvent (ccIO_RotateObject),
		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cRotationEvent (ccIO_RotateObjectGlobal),
		(cEventConsumer *)this);
//	mDispatcher->UnsubscribeFromEvent (
//		cMaterialEvent (ccIO_SetObjectMaterial),
//		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cEvent (ccIO_ShowObject),
		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cEvent (ccIO_HideObject),
		(cEventConsumer *)this);
	mDispatcher->UnsubscribeFromEvent (
		cEvent (ccIO_ToggleVisibility),
		(cEventConsumer *)this);
#if DEBUG_SUBSCRIBE
	cerr << GetName () << ": unsubscribed from active events " << endl;
	mDispatcher->DisplayRegistered ();
#endif
}


/**
 * unsubscribe from "passive" events
 	*/
void cInteractiveObject::UnsubscribeFromPassiveEvents ()
{
	// get rid of our activation subscription
	mDispatcher->UnsubscribeFromEvent (
		cAddressEvent (ccIO_SelectObject, (void *)this),
		(cEventConsumer *)this);
}

#undef DEBUG_RECEIVE
#undef DEBUG_SUBSCRIBE
