/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file SG_IObject.h
 *  \ingroup bgesg
 */
 
#ifndef __SG_IOBJECT_H__
#define __SG_IOBJECT_H__

#include "SG_QList.h"
#include <vector>

// used for debugging: stage of the game engine main loop at which a Scenegraph modification is done
enum SG_Stage
{
	SG_STAGE_UNKNOWN = 0,
	SG_STAGE_NETWORK,
	SG_STAGE_NETWORK_UPDATE,
	SG_STAGE_PHYSICS1,
	SG_STAGE_PHYSICS1_UPDATE,
	SG_STAGE_CONTROLLER,
	SG_STAGE_CONTROLLER_UPDATE,
	SG_STAGE_ACTUATOR,
	SG_STAGE_ACTUATOR_UPDATE,
	SG_STAGE_ANIMATION_UPDATE,
	SG_STAGE_PHYSICS2,
	SG_STAGE_PHYSICS2_UPDATE,
	SG_STAGE_SCENE,
	SG_STAGE_RENDER,
	SG_STAGE_CONVERTER,
	SG_STAGE_CULLING,
	SG_STAGE_MAX
};

extern SG_Stage gSG_Stage;

inline void SG_SetActiveStage(SG_Stage stage)
{
	gSG_Stage = stage;
}
	


class SG_Controller;
class SG_IObject;

typedef std::vector<SG_Controller*> SGControllerList;

typedef void* (*SG_ReplicationNewCallback)(
	SG_IObject* sgobject,
	void*	clientobj,
	void*	clientinfo
);

typedef void* (*SG_DestructionNewCallback)(
	SG_IObject* sgobject,
	void*	clientobj,
	void*	clientinfo
);

typedef void  (*SG_UpdateTransformCallback)(
	SG_IObject* sgobject,
	void*	clientobj,
	void*	clientinfo
);

typedef bool  (*SG_ScheduleUpdateCallback)(
	SG_IObject* sgobject,
	void*	clientobj,
	void*	clientinfo
);

typedef bool  (*SG_RescheduleUpdateCallback)(
	SG_IObject* sgobject,
	void*	clientobj,
	void*	clientinfo
);


/**
 * SG_Callbacks hold 2 call backs to the outside world.
 * The first is meant to be called when objects are replicated.
 * And allows the outside world to synchronize external objects
 * with replicated nodes and their children.
 * The second is called when a node is destroyed and again
 * is their for synchronization purposes
 * These callbacks may both be NULL. 
 * The efficacy of this approach has not been proved some 
 * alternatives might be to perform all replication and destruction
 * externally. 
 * To define a class interface rather than a simple function
 * call back so that replication information can be transmitted from 
 * parent->child. 
 */
struct	SG_Callbacks
{
	SG_Callbacks(
	):
		m_replicafunc(NULL),
		m_destructionfunc(NULL),
		m_updatefunc(NULL),
		m_schedulefunc(NULL),
		m_reschedulefunc(NULL)
	{
	}

	SG_Callbacks(
		SG_ReplicationNewCallback repfunc,
		SG_DestructionNewCallback destructfunc,
		SG_UpdateTransformCallback updatefunc,
		SG_ScheduleUpdateCallback schedulefunc,
		SG_RescheduleUpdateCallback reschedulefunc
	): 
		m_replicafunc(repfunc),
		m_destructionfunc(destructfunc),
		m_updatefunc(updatefunc),
		m_schedulefunc(schedulefunc),
		m_reschedulefunc(reschedulefunc)
	{
	}

	SG_ReplicationNewCallback	m_replicafunc;
	SG_DestructionNewCallback	m_destructionfunc;
	SG_UpdateTransformCallback	m_updatefunc;
	SG_ScheduleUpdateCallback	m_schedulefunc;
	SG_RescheduleUpdateCallback m_reschedulefunc;
};

/**
 * base object that can be part of the scenegraph.
 */
class SG_IObject : public SG_QList
{
private :

	void*	m_SGclientObject;
	void*	m_SGclientInfo;
	SG_Callbacks m_callbacks;
	SGControllerList	m_SGcontrollers;

public:
	virtual ~SG_IObject();


	/**
	 * Add a pointer to a controller allocated on the heap, to 
	 * this node. This memory for this controller becomes the 
	 * responsibility of this class. It will be deleted when
	 * this object is deleted.
	 */
	
		void
	AddSGController(
		SG_Controller* cont
	);

	/**
	 * Remove a pointer to a controller from this node.
	 * This does not delete the controller itself! Be careful to
	 * avoid memory leaks.
	 */
		void
	RemoveSGController(
		SG_Controller* cont
	);

	/** 
	 * Clear the array of pointers to controllers associated with 
	 * this node. This does not delete the controllers themselves!
	 * This should be used very carefully to avoid memory
	 * leaks.
	 */
	
		void
	RemoveAllControllers(
	); 

	/// Needed for replication

	/** 
	 * Return a reference to this node's controller list. 
	 * Whilst we don't wish to expose full control of the container
	 * to the user we do allow them to call non_const methods
	 * on pointers in the container. C++ topic: how to do this in
	 * using STL? 
	 */

	SGControllerList& GetSGControllerList()
	{ 
		return m_SGcontrollers; 
	}

	/**
	 * 
	 */
	SG_Callbacks& GetCallBackFunctions()
	{
		return m_callbacks;
	}
	
	/**
	 * Get the client object associated with this
	 * node. This interface allows you to associate
	 * arbitrary external objects with this node. They are
	 * passed to the callback functions when they are 
	 * activated so you can synchronize these external objects
	 * upon replication and destruction
	 * This may be NULL.
	 */

	inline const void* GetSGClientObject() const
	{
		return m_SGclientObject;
	}

	inline void* GetSGClientObject()
	{ 
		return m_SGclientObject;
	}

	/**
	 * Set the client object for this node. This is just a 
	 * pointer to an object allocated that should exist for 
	 * the duration of the lifetime of this object, or until
	 * this function is called again.
	 */
	
	void SetSGClientObject(void* clientObject)
	{
		m_SGclientObject = clientObject;
	}


	/* needed for scene switching */
	inline const void* GetSGClientInfo() const
	{
		return m_SGclientInfo;
	}
	inline void* GetSGClientInfo()
	{
		return m_SGclientInfo;
	}
	void SetSGClientInfo(void* clientInfo)
	{
		m_SGclientInfo = clientInfo;
	}


	/** 
	 * Set the current simulation time for this node.
	 * The implementation of this function runs through
	 * the nodes list of controllers and calls their SetSimulatedTime methods
	 */
 
	void SetControllerTime(double time);
	
	virtual 
		void
	Destruct(
	) = 0;

protected :

		bool
	ActivateReplicationCallback(
		SG_IObject *replica
	)
	{
		if (m_callbacks.m_replicafunc)
		{
			// Call client provided replication func
			if (m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo) == NULL)
				return false;
		}
		return true;
	}


		void
	ActivateDestructionCallback(
	)
	{
		if (m_callbacks.m_destructionfunc)
		{
			// Call client provided destruction function on this!
			m_callbacks.m_destructionfunc(this,m_SGclientObject,m_SGclientInfo);
		}
		else
		{
			// no callback but must still destroy the node to avoid memory leak
			delete this;
		}
	}
	
		void
	ActivateUpdateTransformCallback(
	)
	{
		if (m_callbacks.m_updatefunc)
		{
			// Call client provided update func.
			m_callbacks.m_updatefunc(this, m_SGclientObject, m_SGclientInfo);
		}
	}

		bool
	ActivateScheduleUpdateCallback(
	)
	{
		// HACK, this check assumes that the scheduled nodes are put on a DList (see SG_Node.h)
		// The early check on Empty() allows up to avoid calling the callback function
		// when the node is already scheduled for update.
		if (Empty() && m_callbacks.m_schedulefunc)
		{
			// Call client provided update func.
			return m_callbacks.m_schedulefunc(this, m_SGclientObject, m_SGclientInfo);
		}
		return false;
	}

		void
	ActivateRecheduleUpdateCallback(
	)
	{
		if (m_callbacks.m_reschedulefunc)
		{
			// Call client provided update func.
			m_callbacks.m_reschedulefunc(this, m_SGclientObject, m_SGclientInfo);
		}
	}


	SG_IObject(
		void* clientobj,
		void* clientinfo,
		SG_Callbacks& callbacks
	);

	SG_IObject(
		const SG_IObject &other
	);


#ifdef WITH_CXX_GUARDEDALLOC
	MEM_CXX_CLASS_ALLOC_FUNCS("GE:SG_IObject")
#endif
};

#endif  /* __SG_IOBJECT_H__ */
