/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**@pkg cObject.cVisibleObject.cInteractiveObject.cLight*/
/**
 * a class for managing an OpenGL light
 *
 * #include "cLight.hh"
 *
 * -lMesaGL or -lGL
 *
 * This is the base class for lights and represents a point light.
 * GL_x_ATTENUATION is not (yet) supported. (It's not terribly useful
 * anyway.)
 *
 * @see cInteractiveObject
 * @pkgdoc cObject.cVisibleObject.cInteractiveObject.cLight
 */

#ifndef cLight_hh
#define cLight_hh

// include class cVisibleObject to derive from
#include "cInteractiveObject.hh"

// include class cColor representing an RGB(A) color for use
// with OpenGL
#include "cADSEColor.hh"

// include common project macros, definitions and headers
#include "common.hh"

// I don't want to include glu.h here; we just need a type.
typedef struct GLUquadricObj GLUquadricObj;

/**
 * A class for managing OpenGL lights.
 	*
	* Lights are very special since there might be only a limited amount
	* of them active at once. We need to check that before turning a
	* light on (that's what the static variables are for).
	*
	* It's a special object since it does not draw itself, but only
	* is glEnabled() during SetUpForDrawing and glDisabled() during
	* CleanUpAfterDrawing() (of course, the light needs to be turned on).
	*
	* It might (and actually is) derived from this class, e.g. for
	* providing a directional and a spot light.
	*
	* Note that cLights are treatened like all other cObjects as far as they
	* might have siblings and take part in the normal scene hierarchy.
	*
	* The inherited mRotation quaternion is not used.
	*/
class cLight 
:	public cInteractiveObject
{
public:

	/**
	 * an extra type for the light status
	 	*/
	typedef enum eLightState {
		LIGHT_OFF,
		LIGHT_ON
	} teLightState;

	/**
	 * default constructor
	 	*/
	cLight (
		cEventDispatcher *disp,
		const char *name = NULL);

#if DEBUG
	/**
	 * copy constructor (inherited)
	 	*/
	cLight (const cLight &);
#endif

	/**
	 * constructor with initialization of position and name
	 	* @param pos initial position of light source
		* @param state initial state of light source (default: on)
		* @param name name of light source (optional)
	 	*/
	cLight (
		cEventDispatcher *disp,
		const cVertex &pos, 
		const char *name = NULL);

	/**
	 * constructor with initialization of position, state and name
	 	* @param pos initial position of light source
		* @param state initial state of light source (default: on)
		* @param name name of light source (optional)
	 	*/
	cLight (
		cEventDispatcher *disp,
		const cVertex &pos, 
		const teLightState state = LIGHT_ON, 
		const char *name = NULL);

	/**
	 * constructor with initialization of position, color, state 
	 * and name
	 	* @param pos initial position of light source
		* @param col color of the light source (cADSEColor object)
		*        (emissive color component is not used)
		* @param state initial state of light source (default: on)
		* @param name name of light source (optional)
	 	*/
	cLight (
		cEventDispatcher *disp,
		const cVertex &pos, 
		const cADSEColor &col,
		const teLightState state = LIGHT_ON, 
		const char *name = NULL);

	/**
	 * destructor
	 	*/
	virtual ~cLight ();

// OPERATIONS
	// note: translating the light is obtained via functions inherited from
	// cVisibleObject (@see cVisibleObject::MoveBy, cVisibleObject::MoveTo)

// ACCESS
	/**
	 * enable the light
		* @param state teLightState - LIGHT_ON or LIGHT_OFF
	 	*/
	void SetState (teLightState state);

	/**
	 * query light status
	 	* @return bool stating whether the light is on or off
	 	*/
	bool IsOn () const;

	/**
	 * set ambient color
	 	* @param c new ambient color of light
	 	*/
	virtual void SetAmbientColor (const cColor &c);

	/**
	 * set diffuse color
	 	* @param c new diffuse color of light
	 	*/
	virtual void SetDiffuseColor (const cColor &c);

	/**
	 * set specular color
	 	* @param c new specular color of light
	 	*/
	virtual void SetSpecularColor (const cColor &c);


	/**
	 * event receiver - we listen for switchon/switchoff events
		*
		* @return 0 - event accepted, -1 - event not
		* accepted
		*/
	virtual int ReceiveEvent (const cEvent &event);

	/**
	 * overload cVisibleObject::MoveTo since we want to ignore
	 * mRotation while moving (it's used to determine direction only)
	 	*/
	virtual void MoveTo (const cVertex &v);

	/**
	 * overload cVisibleObject::MoveBy since we want to ignore
	 * mRotation while moving (it's used to determine direction only)
	 	*/
	virtual void MoveBy (const cVertex &v);

protected:
	/**
	 * set up for drawing (overloaded from cVisibleObject)
	 	*
		* Lights should follow the following behaviour:
		* 1. If you change a standard parameter for a light,
		*    undo it in Deactivate()
		* 2. Position and {Ambient,Diffuse,Specular}Color do not
		*    count for 1.
	 	*/
	virtual void Activate ();

	/**
	 * this one performs the actual set up of the light
		*
		* (called by Activate()); separeted to simplify
		* cDirectionLight
		*/
	virtual void ActivateLight ();

	/**
	 * suppress transformation
	 	*/
	virtual void Transform ();

	/**
	 * A light is a visible object therefore it needs a drawing function
	 	*
		* It is used when the light is shown.
	 	*/
	virtual void DrawThisObject ();

	/**
	 * clean up after drawing (overloaded from cVisibleObject)
	 	*/
	virtual void Deactivate ();

	/**
	 * perform steps neccessary before freeing the light slot
	 * (e.g. disabling the light)
	 	*/
	virtual void DeactivateLight ();

	/**
	 * aquire a light slot (stored in mLightSlot)
	    * @return -1 on error (no free slot), 0 on success
		*/
	virtual int AllocateLightSlot ();

	/**
	 * free the light slot
	    * @return -1 on error (slot was not allocated), 0 on success
		*/
	virtual int FreeLightSlot ();

	/**
	 * we want to get switchon/switchoff events if we're selected
		*/
	virtual void SubscribeToActiveEvents ();

	/**
	 * unsubscribe from switchon/switchoff if we get deselected
		*/
	virtual void UnsubscribeFromActiveEvents ();

	/**
	 * identify an instance of this class if it's got no name
	 	*/
	virtual const char *GetDefaultName () const
	{
		return "cLight";
	}

	/**
	 * current status of light (LIGHT_ON or LIGHT_OFF)
	 	*/
	teLightState mState;
	
	/**
	 * ambient color component of light
	 	*/
	cADSEColor mLightColor;

	/**
	 * keep track of status of OpenGL lights
	 	*
		* As OpenGL allows 8 lights at once, we need to keep track
		* of which lights are currently in use and which are not.
		*
		* At runtime, to enable a light, we look for a free "slot"
		* and use it for our light source.
		*/
	static teLightState mgLightSlots[8];

	/**
	 * keep track of how many lights are currently active
	 	*/
	static int mgActiveLights;

	/**
	 * actually used light slot (-1 = no slot used)
	 	*/
	int mLightSlot;

	/**
	 * only used if the light source is to be drawn
	 	*/
	GLUquadricObj *mQuadric;
};

#endif	// ifndef cLight_hh
