//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/class.h                          $
// $Revision:: 17                                                             $
//   $Author:: Jimdose                                                        $
//     $Date:: 10/25/98 11:53p                                                $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/class.h                               $
// 
// 17    10/25/98 11:53p Jimdose
// added EXPORT_TEMPLATE
// 
// 16    10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// 
// 15    10/07/98 11:42p Jimdose
// Got savegames working
// 
// 14    9/24/98 1:49a Jimdose
// Added DisplayMemoryUsage
// 
// 13    9/21/98 2:15a Jimdose
// Moved non-type specific code in SafePtr to SafePtrBase to help with save
// games
// 
// 12    9/03/98 9:08p Jimdose
// Overrided == and != for SafePtr
// 
// 11    6/27/98 9:18p Jimdose
// Made lookup for event responses for faster processing
// 
// 10    5/24/98 4:48p Jimdose
// Made char *'s const
// 
// 9     5/11/98 8:06p Jimdose
// Added SafePtr
// 
// 8     5/08/98 2:51p Jimdose
// Added archiving functions
// 
// 7     3/23/98 1:33p Jimdose
// Revamped event and command system
// 
// 6     3/02/98 8:49p Jimdose
// Changed CLASS_PROTOTYPE to only take the classname
// 
// 5     2/03/98 10:53a Jimdose
// Updated to work with Quake 2 engine
// Made class registration automatic
// 
// 4     1/22/98 6:50p Jimdose
// Made Q2 compatible
// 
// 2     9/26/97 6:13p Jimdose
// Added standard Ritual headers
//
// DESCRIPTION:
// Base class that all classes that are used in conjunction with Sin should
// be based off of.  Class gives run-time type information about any class
// derived from it.  This is really handy when you have a pointer to an object 
// that you need to know if it supports certain behaviour.
// 

#ifndef __CLASS_H__
#define __CLASS_H__

#include "g_local.h"

class Class;
class Event;
class Archiver;

typedef void ( Class::*Response )( Event *event );
typedef struct 
	{
	Event		*event;
	Response	response;
	} ResponseDef;

/***********************************************************************

  ClassDef

***********************************************************************/

class ClassDef
	{
	public:
		const char	*classname;
		const char	*classID;
		const char	*superclass;
		void			*( *newInstance )( void );
		int			classSize;
		ResponseDef *responses;
		int			numEvents;
		Response		**responseLookup;
		ClassDef		*super;
		ClassDef		*next;
		ClassDef		*prev;

		ClassDef();
		~ClassDef();
		ClassDef( const char *classname, const char *classID, const char *superclass, 
			ResponseDef *responses, void *( *newInstance )( void ), int classSize );
		void	BuildResponseList( void );
	};

/***********************************************************************

  SafePtr

***********************************************************************/

class SafePtrBase;

class Class;

class SafePtrBase
	{
   private:
		void	AddReference( Class *ptr );
		void	RemoveReference( Class *ptr );

	protected:
		SafePtrBase *prevSafePtr;
		SafePtrBase *nextSafePtr;
		Class       *ptr;

	public:
                  SafePtrBase();
		virtual     ~SafePtrBase();
		void	      InitSafePtr( Class *newptr );
      void	      Clear( void );
	};

/***********************************************************************

  Class

***********************************************************************/

#define CLASS_DECLARATION( nameofsuperclass, nameofclass, classid )	\
	ClassDef nameofclass::ClassInfo												\
		(																					\
		#nameofclass, classid, #nameofsuperclass,								\
		nameofclass::Responses, nameofclass::_newInstance,					\
		sizeof( nameofclass )														\
		);																					\
	void *nameofclass::_newInstance( void )				\
		{																					\
		return new nameofclass;														\
		}																					\
	ClassDef *nameofclass::classinfo( void )								   \
		{																					\
		return &( nameofclass::ClassInfo );										\
		}

#define CLASS_PROTOTYPE( nameofclass )											\
	public:																				\
	static	ClassDef			ClassInfo;											\
	static	void				*nameofclass::_newInstance( void );			\
	virtual	ClassDef			*nameofclass::classinfo( void );			   \
	static	ResponseDef		nameofclass::Responses[];

class Class
	{
   private:
		SafePtrBase	 *SafePtrList;
		friend class SafePtrBase;

	public:
		CLASS_PROTOTYPE( Class );
		void * operator	new( size_t );
		void operator		delete( void * );

								Class();
		virtual				~Class();
		virtual void		Archive( Archiver &arc );
		virtual void		Unarchive( Archiver &arc );
		void					warning( const char *function, const char *fmt, ... );
		void					error( const char *function, const char *fmt, ... );
		qboolean				inheritsFrom( ClassDef *c );
		qboolean				inheritsFrom( const char *name );
		qboolean				isInheritedBy( const char *name );
		qboolean				isInheritedBy( ClassDef *c );
		const char			*getClassname( void );
		const char			*getClassID( void );
		const char			*getSuperclass( void );
		void					*newInstance( void );
	};

void			BuildEventResponses( void );
ClassDef	*getClassForID( const char *name );
ClassDef	*getClass( const char *name );
ClassDef	*getClassList( void );
void			listAllClasses( void );
void			listInheritanceOrder( const char *classname );
qboolean	checkInheritance( ClassDef *superclass, ClassDef *subclass );
qboolean	checkInheritance( ClassDef *superclass, const char *subclass );
qboolean	checkInheritance( const char *superclass, const char *subclass );
void       DisplayMemoryUsage( void );

inline qboolean Class::inheritsFrom
	( 
	ClassDef *c 
	)

	{
	return checkInheritance( c, classinfo() );
	}

inline qboolean Class::isInheritedBy
	( 
	ClassDef *c 
	)

	{
	return checkInheritance( classinfo(), c );
	}

// The lack of a space between the ")" and "inheritsFrom" is intentional.
// It allows the macro to compile like a function call.  However, this
// may cause problems in some compilers (like gcc), so we may have to
// change this to work like a normal macro with the object passed in
// as a parameter to the macro.
#define isSubclassOf( classname )inheritsFrom( &classname::ClassInfo )
#define isSuperclassOf( classname )isInheritedBy( &classname::ClassInfo )

/***********************************************************************

  SafePtr

***********************************************************************/

inline SafePtrBase::SafePtrBase()
	{
   prevSafePtr = NULL;
	nextSafePtr = NULL;
   ptr = NULL;
   }

inline SafePtrBase::~SafePtrBase()
	{
   Clear();
   }

inline void SafePtrBase::Clear
   (
   void
   )

	{
	if ( ptr )
		{
		RemoveReference( ptr );
		ptr = NULL;
		}
	}

inline void SafePtrBase::InitSafePtr
   (
   Class *newptr
   )

	{
	if ( ptr != newptr )
		{
		if ( ptr )
			{
			RemoveReference( ptr );
			}

		ptr = newptr;
	   if ( ptr == NULL )
		   {
		   return;
   	   }

      AddReference( ptr );
      }
	}

inline void SafePtrBase::AddReference
	(
	Class *ptr
	)

	{
	if ( !ptr->SafePtrList )
		{
		ptr->SafePtrList = this;
		LL_Reset( this, nextSafePtr, prevSafePtr );
		}
	else
		{
		LL_Add( ptr->SafePtrList, this, nextSafePtr, prevSafePtr );
		}
	}

inline void SafePtrBase::RemoveReference
	(
	Class *ptr
	)

	{
	if ( ptr->SafePtrList == this )
		{
		if ( ptr->SafePtrList->nextSafePtr == this )
			{
			ptr->SafePtrList = NULL;
			}
		else
			{
			ptr->SafePtrList = nextSafePtr;
			LL_Remove( this, nextSafePtr, prevSafePtr );
			}
		}
	else
		{
		LL_Remove( this, nextSafePtr, prevSafePtr );
		}
	}

template<class T>
class SafePtr : public SafePtrBase
	{
	public:
		SafePtr( T* objptr = 0 );
		SafePtr( const SafePtr& obj );

		SafePtr& operator=( const SafePtr& obj );
		SafePtr& operator=( T * const obj );

#if 0
		friend int operator==( SafePtr<T> a, T *b );
		friend int operator!=( SafePtr<T> a, T *b );
		friend int operator==( T *a, SafePtr<T> b );
		friend int operator!=( T *a, SafePtr<T> b );
//		friend int operator== (SafePtr<T> a, T b);
#else
	inline int operator==( T *a ) { return ptr == a; }
	inline int operator!=( T *a ) { return ptr != a; }
	
	inline int operator==( SafePtr &a ) { return ptr == a.ptr; }
	inline int operator!=( SafePtr &a ) { return ptr != a.ptr; }
#endif
      operator	T*() const;
		T* operator->() const;
		T& operator*() const;
	T* Ptr(void); //### so I can type cast it when I need to
	};

template<class T>
inline SafePtr<T>::SafePtr( T* objptr )
{
	InitSafePtr( objptr );
}

template<class T>
inline SafePtr<T>::SafePtr( const SafePtr& obj )
{
	InitSafePtr( obj.ptr );
}

template<class T>
inline SafePtr<T>& SafePtr<T>::operator=( const SafePtr& obj )
{
   InitSafePtr( obj.ptr );
	return *this;
}

template<class T>
inline SafePtr<T>& SafePtr<T>::operator=( T * const obj )
{
   InitSafePtr( obj );
	return *this;
}


template<class T, class T2>
inline int operator==(T* a,	SafePtr<T2> &b)
{
	return b == static_cast<T2 *>(a);
}

template<class T, class T2>
inline int operator!=(T* a, SafePtr<T2> &b)
{
	return b != static_cast<T2 *>(a);
}


template<class T, class T2>
inline int operator==(SafePtr<T2> &b, T* a)
{
	return b == static_cast<T2 *>(a);
}

template<class T, class T2>
inline int operator!=(SafePtr<T2> &b,T* a)
{
	return b != static_cast<T2 *>(a);
}


template<class T>
inline SafePtr<T>::operator T*() const
{
	return ( T * )ptr;
}

template<class T>
inline T* SafePtr<T>::operator->() const
{
	return ( T * )ptr;
}

template<class T>
inline T& SafePtr<T>::operator*() const
{
	return *( T * )ptr;
}

#ifdef EXPORT_TEMPLATE
template class SafePtr<Class>;
#endif

//### so I can type cast this like a regular pointer
template<class T>
inline T* SafePtr<T>::Ptr(void)
{
	return (T *)ptr;
}

typedef SafePtr<Class> ClassPtr;

#include "archive.h"

#endif /* class.h */
