/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**
 * implementation of class cVisibleObject
 *
 * #include "cVisibleObject.hh"
 *
 */

// include STL templates for algorithms
#include <algo.h>
// include C strings
#include <string.h>
// include free() to get rid of mName
#include <malloc.h>

#include "cVisibleObject.hh"
#include "common.hh"

//#if DEBUG
#include "iocVertex.hh"
#include "iocMatrix.hh"
//#endif

/**
 * constructor with initialization for name (default constructor)
	* @param name name to identify the object (optional)
	*/
cVisibleObject::cVisibleObject (const char *name)
:	mVisible (true),
	mMaterial(NULL)
{
	SetName (name);	// first set name to make debug message below more fancy
	ENTER_OBJECT_METHOD("cVisibleObject::cVisibleObject (name)");
}

/**
 * copy constructor
 	*/
cVisibleObject::cVisibleObject (const cVisibleObject &source)
// use inherited copy constructor (copies childs as well)
:	cObject ((const cObject &)source), 
	mRotation (source.mRotation),
	mPosition (source.mPosition),
	mVisible (source.mVisible),
	mMaterial(mMaterial),
	mGlobalTransformation (source.mGlobalTransformation)
{
	SetName (source.GetName ());

	ENTER_OBJECT_METHOD("cVisibleObject::cVisibleObject (const cVisibleObject &source)");

}


/**
 * constructor with initialization for position and name
	* @param pos initial object position
	* @param name name to identify the object (optional)
	*/
cVisibleObject::cVisibleObject (const cVertex &pos, const char *name)
:	mPosition (pos),
	mVisible (true),
	mMaterial (NULL)
{
	ENTER_OBJECT_METHOD("cVisibleObject::cVisibleObject (pos,name)");

	SetName (name);
}


/**
 * constructor with initialization for position, rotation and name
	* @param pos initial object position
	* @param rot initial object rotation
	* @param name name to identify the object (optional)
	*/
cVisibleObject::cVisibleObject (
		const cVertex &pos, 
		const cQuaternion &rot, 
		const char *name)
:	cObject (name),
	mRotation (rot),
	mPosition (pos),
	mVisible (true),
	mMaterial (NULL)
{
	ENTER_OBJECT_METHOD("cVisibleObject::cVisibleObject (pos,rot,name)");
}

/**
 * destructor
 	*/
cVisibleObject::~cVisibleObject ()
{
	ENTER_OBJECT_METHOD("cVisibleObject::~cVisibleObject()");
}


/**
 * initialization function
 	*/
int cVisibleObject::Init ()
{
	ENTER_OBJECT_METHOD ("cVisibleObject::Init ()");

	// initialize material if used
	if (mMaterial != NULL)
	{
		if (mMaterial->Init () != 0)
		{
			return -1;
		}

	}

	return cObject::Init ();
}


/**
 * draw the object (plus all it's siblings)
 	*/
void cVisibleObject::Activate ()
{
	ENTER_OBJECT_METHOD ("cVisibleObject::Activate ()");

#if 0
	int sd;

	glGetIntegerv (GL_MODELVIEW_STACK_DEPTH, &sd);

	cerr << GetName () << ": cVisibleObject::Activate () - stack depth is " << sd << endl;
#endif

	// take care of the matrix stack
	glPushMatrix ();

	if (mMaterial != NULL)
	{
		mMaterial->StartMaterialUse ();
	}

	Transform ();

	if (mVisible)
		DrawThisObject ();	// perform actual drawing

	// use material for this object _only_
	if (mMaterial != NULL)
	{
		mMaterial->EndMaterialUse ();
	}

	// invoke inherited Activate() to activate all childs
	// (also calls Deactivate())
	cObject::Activate ();
}


/**
 * perform transformation given by mRotation and mPosition
 	*/
void cVisibleObject::Transform ()
{
	ENTER_OBJECT_METHOD ("cVisibleObject::Transform ()");

#if 0
	glGetFloatv (GL_MODELVIEW_MATRIX, mGlobalTransformation);
	cerr << GetName () << ": global matrix before transformation is " << mGlobalTransformation;
#endif

	// prevent allocation of a matrix every time
	static cMatrix transformation_matrix;
	
	mRotation.GetAsMatrix (transformation_matrix);

	transformation_matrix.Translate (mPosition);

#if 0
	cerr << GetName () << ": transf. matrix is " << transformation_matrix;
#endif

	// note: matrices are stored column-first as OpenGL wants it that way
	glMultMatrixf ((GLfloat *)&transformation_matrix.mMatrix);
	glGetFloatv (GL_MODELVIEW_MATRIX, mGlobalTransformation);

#if DEBUG
	cerr << GetName () << ": position is " << mPosition;
	cerr << GetName () << ": global transformation matrix is " << mGlobalTransformation;
#endif
}

/**
 * perform actual drawing of the object
 * (nedds to be overloaded to get any use)
 	*/
void cVisibleObject::DrawThisObject ()
{
#if DEBUG
//	glBegin (GL_POINTS);
//	glVertex3f (0, 0, 0);
//	glEnd ();
#endif
}

/**
 * actions to perform after drawing the object and it's siblings
	*/
void cVisibleObject::Deactivate ()
{
	ENTER_OBJECT_METHOD ("cVisibleObject::Deactivate ()");

	// dont forget our matrix stack
	glPopMatrix ();

#if 0
	int sd;

	glGetIntegerv (GL_MODELVIEW_STACK_DEPTH, &sd);

	cerr << GetName () << ": cVisibleObject::Deactivate () - stack depth is " << sd << endl;
#endif

	cObject::Deactivate ();
}


/**
 * move object's coordinate system to given position
 	* @param pos cVertex with new position of coordinate origin
	*/
void cVisibleObject::MoveTo (const cVertex &pos)
{
	mPosition = (mRotation*pos);
}

/**
 * move object's coordinate system by given amount
 	* @param pos cVertex with new position of coordinate origin
	*
	*/
void cVisibleObject::MoveBy (const cVertex &pos)
{
	mPosition += (mRotation*pos);
}


/**
 * rotate object angle degrees around given vector 
 * ("adds" to current rotation)
 	* @param angle angle in degree
	* @param axis vector to rotate around
	*/
void cVisibleObject::RotateBy (
	const GLfloat angle,
	const cVertex &axis)
{
	mRotation *= cQuaternion (angle, axis);
	//mRotation.Normalize ();
}

/**
 * rotate object by applying a quaternion
 * ("adds" to current rotation)
 	* @param quaternion quaternion to apply
	*/
void cVisibleObject::RotateBy (const cQuaternion &quaternion)
{
	mRotation *= quaternion;
	//mRotation.Normalize ();
}


/**
 * set rotation to given values (replaces current rotation)
 	* @param angle angle in degree
	* @param axis vector to rotate around
	*/
void cVisibleObject::SetRotation (
	const GLfloat angle,
	const cVertex &axis)
{
	mRotation = cQuaternion (angle, axis);
}

/**
 * set rotation to given value (replaces current rotation)
 	* @param quaternion angle in degree
	*/
void cVisibleObject::SetRotation (const cQuaternion &new_rotation)
{
	mRotation = new_rotation;
	//mRotation.Normalize ();
}

/**
 * set material to use
 */
void cVisibleObject::UseMaterial (cMaterial *material)
{
	DontUseMaterial ();

	// copy the material
	mMaterial = material;
}

/**
 * throw away material to use
 */
void cVisibleObject::DontUseMaterial ()
{
	if (mMaterial != NULL)
	{
		mMaterial = NULL;
	}

}

