//----------------------------------------------------------------------------- 
//
//  $Logfile:: /Code/DLLs/game/class.cpp                                      $
// $Revision:: 17                                                             $
//   $Author:: Steven                                                         $
//     $Date:: 10/13/03 9:43a                                                 $
//
// 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.
//
//
// DESCRIPTION:
// Base class that all classes that are used in conjunction with the game 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.
//
// WARNING: This file is shared between game, cgame and possibly the user interface.
// It is instanced in each one of these directories because of the way that SourceSafe works.
//

#include <string.h>
#include <stdio.h>
#include <stdarg.h>


#if defined( GAME_DLL )

#include "g_local.h"

#elif defined ( CGAME_DLL )

#include "cg_local.h"
#include "listener.h"
#include <qcommon/qcommon.h>

#else

#include "listener.h"
#include <qcommon/qcommon.h>

#endif

#include "class.h"
#include "Linklist.h"
#include <qcommon/output.h>

int totalmemallocated = 0;
int numclassesallocated = 0;

static ClassDef *classlist = NULL;


ClassDef::ClassDef()
{
	this->classname		= NULL;
	this->classID			= NULL;
	this->superclass		= NULL;
	this->responses		= NULL;
	this->numEvents		= 0;
	this->responseLookup = NULL;
	this->newInstance		= NULL;
	this->classSize		= 0;
	this->super				= NULL;
	this->prev				= this;
	this->next				= this;
}

ClassDef::ClassDef( const char *classname, const char *classID, const char *superclass, ResponseDef<Class> *responses,
		void *( *newInstance )( void ), int classSize )
{
	ClassDef *node;
	
	if ( classlist == NULL )
	{
		classlist = new ClassDef;
	}
	
	this->classname			= classname;
	this->classID				= classID;
	this->superclass			= superclass;
	this->responses			= responses;
	this->numEvents			= 0;
	this->responseLookup		= NULL;
	this->newInstance			= newInstance;
	this->classSize			= classSize;
	this->super					= getClass( superclass );
	
	// It's not uncommon for classes to not have a class id, so just set it
	// to an empty string so that we're not checking for it all the time.
	if ( !classID )
	{
		this->classID = "";
	}
	
	// Check if any subclasses were initialized before their superclass
	for( node = classlist->next; node != classlist; node = node->next )
	{
		if ( ( node->super == NULL ) && ( !Q_stricmp( node->superclass, this->classname ) ) &&
			( Q_stricmp( node->classname, "Class" ) ) )
		{
			node->super	= this;
		}
	}
	
	// Add to front of list
	LL_Add( classlist, this, prev, next );
}

void ClassDef::Shutdown( void )
{
	if ( responseLookup )
	{
		delete[] responseLookup;
		responseLookup = NULL;
	}
}

ClassDef::~ClassDef()
{
	ClassDef *node;
	
	if ( classlist != this )
	{
		LL_Remove( this, prev, next );
		
		// Check if any subclasses were initialized before their superclass
		for( node = classlist->next; node != classlist; node = node->next )
		{
			if ( node->super == this )
			{
				node->super	= NULL;
			}
		}
	}
	else
	{
		// If the head of the list is deleted before the list is cleared, then we may have problems
		assert( this->next == this->prev );
	}
	
	if ( responseLookup )
	{
		delete[] responseLookup;
		responseLookup = NULL;
	}
}

void ClassDef::BuildResponseList( void )
{
	ClassDef		*c;
	ResponseDef<Class> *r;
	int			ev;
	int			i;
	qboolean		*set;
	int			num;
	
	if ( responseLookup )
	{
		delete[] responseLookup;
		responseLookup = NULL;
	}
	
	num = Event::NumEventCommands();
	responseLookup = ( Response ** )new char[ sizeof( Response * ) * num ];
	memset( responseLookup, 0, sizeof( Response * ) * num );
	
	set = new qboolean[ num ];
	memset( set, 0, sizeof( qboolean ) * num );
	
	this->numEvents = num;
	
	for( c = this; c != NULL; c = c->super )
	{
		r = c->responses;
		if ( r )
		{
			for( i = 0; r[ i ].event != NULL; i++ )
			{
				ev = ( int )*r[ i ].event;
				if ( !set[ ev ] )
				{
					set[ ev ] = true;
					if ( r[ i ].response )
					{
						responseLookup[ ev ] = &r[ i ].response;
					}
					else
					{
						responseLookup[ ev ] = NULL;
					}
				}
			}
		}
	}
	
	delete[] set;
}

void BuildEventResponses( void )
{
	ClassDef *c;
	int amount;
	int numclasses;
	
	amount = 0;
	numclasses = 0;
	for( c = classlist->next; c != classlist; c = c->next )
	{
		c->BuildResponseList();
		
		amount += c->numEvents * sizeof( Response * );
		numclasses++;
	}
	
	CLASS_DPrintf( "\n------------------\nEvent system initialized: "
		"%d classes %d events %d total memory in response list\n\n", numclasses, Event::NumEventCommands(), amount );
}

ClassDef *getClassForID( const char *name )
{
	ClassDef *c;
	
	for( c = classlist->next; c != classlist; c = c->next )
	{
		if ( c->classID && !Q_stricmp( c->classID, name ) )
		{
			return c;
		}
	}
	
	return NULL;
}

ClassDef *getClass( const char *name )
{
	ClassDef *c;
	
	for( c = classlist->next; c != classlist; c = c->next )
	{
		if ( !Q_stricmp( c->classname, name ) )
		{
			return c;
		}
	}
	
	return NULL;
}

ClassDef *getClassList( void )
{
	return classlist;
}

void listAllClasses( void )
{
	ClassDef *c;
	
	for( c = classlist->next; c != classlist; c = c->next )
	{
		CLASS_DPrintf( "%s\n", c->classname );
	}
}

void listInheritanceOrder( const char *classname )
{
	ClassDef *cls;
	ClassDef *c;
	
	cls = getClass( classname );
	if ( !cls )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", classname );
		return;
	}
	for( c = cls; c != NULL; c = c->super )
	{
		CLASS_DPrintf( "%s\n", c->classname );
	}
}

qboolean checkInheritance( const ClassDef *superclass, const ClassDef *subclass )
{
	const ClassDef *c;
	
	for( c = subclass; c != NULL; c = c->super )
	{
		if ( c == superclass )
		{
			return true;
		}
	}
	return false;
}

qboolean checkInheritance( ClassDef *superclass, const char *subclass )
{
	ClassDef *c;
	
	c = getClass( subclass );
	if ( c == NULL )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", subclass );
		return false;
	}
	return checkInheritance( superclass, c );
}

qboolean checkInheritance( const char *superclass, const char *subclass )
{
	ClassDef *c1;
	ClassDef *c2;
	
	c1 = getClass( superclass );
	c2 = getClass( subclass );
	if ( c1 == NULL )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", superclass );
		return false;
	}
	if ( c2 == NULL )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", subclass );
		return false;
	}
	return checkInheritance( c1, c2 );
}

void CLASS_Print( FILE *class_file, const char *fmt, ... )
{
	va_list	argptr;
	char		text[ 1024 ];
	
	va_start( argptr, fmt );
	vsprintf( text, fmt, argptr );
	va_end( argptr );
	
	if ( class_file )
		fprintf( class_file, text );
	else
		CLASS_DPrintf( text );
}

CLASS_DECLARATION( NULL, Class, NULL )
{
	{ NULL, NULL }
};

#ifdef NDEBUG

void * Class::operator new( size_t s )
{
	int *p;
	
	if ( s == 0 )
		return 0;
	
	s += sizeof( int );
#if defined( GAME_DLL )
	p = ( int * )gi.Malloc( s );
#elif defined( CGAME_DLL )
	p = ( int * )cgi.Malloc( s );
#else
	p = ( int * )Z_TagMalloc( s, TAG_CLIENT );
#endif
	*p = s;
	totalmemallocated += s;
	numclassesallocated++;
	return p + 1;
}

void Class::operator delete( void *ptr )
{
	int *p;
	
	p = ( ( int * )ptr ) - 1;
	totalmemallocated -= *p;
	numclassesallocated--;
#if defined( GAME_DLL )
	gi.Free( p );
#elif defined( CGAME_DLL )
	cgi.Free( p );
#else
	Z_Free( p );
#endif
}

#else

#ifdef MEMORY_LEAK_TEST
int classindex = 0;
#endif

void * Class::operator new( size_t s )
{
	int *p;
	
	s += sizeof( int ) * 3;
#if defined( GAME_DLL )
	p = ( int * )gi.Malloc( s );
#elif defined( CGAME_DLL )
	p = ( int * )cgi.Malloc( s );
#else
	p = ( int * )Z_TagMalloc( s, TAG_CLIENT );
#endif
	// set memory to a known wrong number
	memset( p, 0xaa, s );
#ifdef MEMORY_LEAK_TEST
	p[ 0 ] = classindex++;
#else
	p[ 0 ] = 0x12348765;
#endif
	*( int * )( ((byte *)p) + s - sizeof( int ) ) = 0x56784321;
	p[ 1 ] = s;
	totalmemallocated += s;
	numclassesallocated++;
	return p + 2;
}

void Class::operator delete( void *ptr )
{
	int *p;
	
	p = ( ( int * )ptr ) - 2;
#ifndef MEMORY_LEAK_TEST
	assert( p[ 0 ] == 0x12348765 );
#endif
	assert( *( int * )( ((byte *)p) + p[ 1 ] - sizeof( int ) ) == 0x56784321 );
	
	totalmemallocated -= p[ 1 ];
	numclassesallocated--;
#if defined( GAME_DLL )
	gi.Free( p );
#elif defined( CGAME_DLL )
	cgi.Free( p );
#else
	Z_Free( p );
#endif
}

#endif

void DisplayMemoryUsage( void )
{
	CLASS_Printf( "Classes %-5d Class memory used: %d\n", numclassesallocated, totalmemallocated );
}

Class::Class()
{
	SafePtrList = NULL;
}

Class::~Class()
{
	ClearSafePointers();
}

#ifdef GAME_DLL

void Class::Archive( Archiver &arc )
{
}

#endif

void Class::ClearSafePointers( void )
{
	while( SafePtrList != NULL )
	{
		SafePtrList->Clear();
	}
}


void Class::warning( const char *function, const char *fmt, ... )
{
	va_list	argptr;
	char		text[ 1024 ];
	
	va_start( argptr, fmt );
	vsprintf( text, fmt, argptr );
	va_end( argptr );
	
	if ( getClassID() )
	{
		CLASS_WDPrintf( "%s::%s : %s\n", getClassID(), function, text );
	}
	else
	{
		CLASS_WDPrintf( "%s::%s : %s\n", getClassname(), function, text );
	}
}

void Class::error( const char *function, const char *fmt, ... )
{
	va_list	argptr;
	char		text[ 1024 ];
	
	va_start( argptr, fmt );
	vsprintf( text, fmt, argptr );
	va_end( argptr );
	
	if ( getClassID() )
	{
		CLASS_Error( ERR_DROP, "%s::%s : %s\n", getClassID(), function, text );
	}
	else
	{
		CLASS_Error( ERR_DROP, "%s::%s : %s\n", getClassname(), function, text );
	}
}

qboolean Class::inheritsFrom( const char *name ) const
{
	ClassDef *c;
	
	c = getClass( name );
	if ( c == NULL )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", name );
		return false;
	}
	return checkInheritance( c, classinfo() );
}

qboolean Class::isInheritedBy( const char *name	) const
{
	ClassDef *c;
	
	c = getClass( name );
	if ( c == NULL )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", name );
		return false;
	}
	return checkInheritance( classinfo(), c );
}

const char *Class::getClassname( void )
{
	ClassDef *cls;
	
	cls = classinfo();
	return cls->classname;
}

const char *Class::getClassID( void )
{
	ClassDef *cls;
	
	cls = classinfo();
	return cls->classID;
}

const char *Class::getSuperclass( void )
{
	ClassDef *cls;
	
	cls = classinfo();
	return cls->superclass;
}

void *Class::newInstance( void )
{
	ClassDef *cls;
	
	cls = classinfo();
	return cls->newInstance();
}

#define MAX_INHERITANCE 64
void ClassEvents( const char *classname, qboolean print_to_disk )
{
	ClassDef		*c;
	ResponseDef<Class> *r;
	int			ev;
	int			i, j;
	qboolean    *set;
	int			num, orderNum;
	Event       **events;
	byte        *order;
	FILE        *class_file;
	str         classNames[ MAX_INHERITANCE ];
	str         class_filename;
	
	c = getClass( classname );
	if ( !c )
	{
		CLASS_WDPrintf( "Unknown class: %s\n", classname );
		return;
	}
	
	class_file = NULL;
	
	if ( print_to_disk )
	{
		class_filename = str ( classname ) + ".txt";
		class_file = fopen( class_filename.c_str(), "w" );
		if ( class_file == NULL )
			return;
	}
	
	num = Event::NumEventCommands();
	
	set = new qboolean[ num ];
	memset( set, 0, sizeof( qboolean ) * num );
	
	events = new Event *[ num ];
	memset( events, 0, sizeof( Event * ) * num );
	
	order = new byte[ num ];
	memset( order, 0, sizeof( byte ) * num );
	
	orderNum = 0;
	for( ; c != NULL; c = c->super )
	{
		if ( orderNum < MAX_INHERITANCE )
		{
			classNames[ orderNum ] = c->classname;
		}
		r = c->responses;
		if ( r )
		{
			for( i = 0; r[ i ].event != NULL; i++ )
			{
				ev = ( int )*r[ i ].event;
				if ( !set[ ev ] )
				{
					set[ ev ] = true;
					
					if ( r[ i ].response )
					{
						events[ ev ] = r[ i ].event;
						order[ ev ] = orderNum;
					}
				}
			}
		}
		orderNum++;
	}
	
	CLASS_Print( class_file, "********************************************************\n" );
	CLASS_Print( class_file, "********************************************************\n" );
	CLASS_Print( class_file, "* All Events For Class: %s\n", classname );
	CLASS_Print( class_file, "********************************************************\n" );
	CLASS_Print( class_file, "********************************************************\n\n" );
	
	for( j = orderNum - 1; j >= 0; j-- )
	{
		CLASS_Print( class_file, "\n********************************************************\n" );
		CLASS_Print( class_file, "* Class: %s\n", classNames[ j ].c_str() );
		CLASS_Print( class_file, "********************************************************\n\n" );
		for( i = 1; i < num; i++ )
		{
			int index;
			
			index = Event::MapSortedEventIndex( i );
			if ( events[ index ] && ( order[ index ] == j ) )
            {
				Event::PrintEventDocumentation( events[ index ], class_file );
            }
		}
	}
	
	if ( class_file != NULL )
	{
		CLASS_DPrintf( "Printed class info to file %s\n", class_filename.c_str() );
		fclose( class_file );
	}
	
	delete[] events;
	delete[] order;
	delete[] set;
}

static int dump_numclasses;
static int dump_numevents;
void DumpClass( FILE * class_file, const char * className, int typeFlag )
{
	ClassDef		*c;
	ResponseDef<Class> *r;
	int			ev;
	int			i;
	int			num;
	Event       **events;
	
	c = getClass( className );
	if ( !c )
	{
		return;
	}
	
	num = Event::NumEventCommands();
	
	events = new Event *[ num ];
	memset( events, 0, sizeof( Event * ) * num );
	
	// gather event responses for this class
	r = c->responses;
	if ( r )
	{
		for( i = 0; r[ i ].event != NULL; i++ )
		{
			ev = ( int )*r[ i ].event;
			if ( r[ i ].response )
			{
				events[ ev ] = r[ i ].event;
			}
		}
	}
	
	CLASS_Print( class_file, "\n");
	if ( c->classID[ 0 ] )
	{
		CLASS_Print( class_file, "<h2> <a name=\"%s\">%s (<i>%s</i>)</a>", c->classname, c->classname, c->classID );
	}
	else
	{
		CLASS_Print( class_file, "<h2> <a name=\"%s\">%s</a>", c->classname, c->classname );
	}
	
	// print out lineage
	for( c = c->super; c != NULL; c = c->super )
	{
		CLASS_Print( class_file, " -> <a href=\"#%s\">%s</a>", c->classname, c->classname );
	}
	CLASS_Print( class_file, "</h2>\n");
	
	dump_numclasses++;
	
	CLASS_Print( class_file, "<BLOCKQUOTE>\n");
	for( i = 1; i < num; i++ )
	{
		int index;
		
		index = Event::MapSortedEventIndex( i );
		if ( events[ index ] )
		{
			Event::PrintEventDocumentation( events[ index ], class_file, true, typeFlag );
			dump_numevents++;
		}
	}
	CLASS_Print( class_file, "</BLOCKQUOTE>\n");
	delete[] events;
}

void DumpAllClasses( int typeFlag, int outputType, const char *filename )
{
	str defaultname;
#if defined( GAME_DLL )
	defaultname = "g_allclasses";
#elif defined( CGAME_DLL )
	defaultname = "cg_allclasses";
#else
	defaultname = "cl_allclasses";
#endif
	
	if ( outputType == OUTPUT_HTML || !outputType )
	{
		HTMLDocFileOutput *hdout = new HTMLDocFileOutput();
		if ( filename )
			hdout->Write(filename, classlist, typeFlag);
		else
			hdout->Write(defaultname, classlist, typeFlag);
		delete hdout;
	}
	
	if ( outputType == OUTPUT_CMD || !outputType )
	{
		ToolDocFileOutput *dout = new ToolDocFileOutput();
		if ( filename )
			dout->Write(filename, classlist, typeFlag);
		else
			dout->Write(defaultname, classlist, typeFlag);
		delete dout;
	}

/*
   int i, j, num, smallest;
	ClassDef *c;
   FILE * class_file;
   str class_filename;
   str class_title;
   str classes[ MAX_CLASSES ];

#if defined( GAME_DLL )
	class_filename = "g_allclasses.html";
   class_title = "Game Module";
#elif defined( CGAME_DLL )
	class_filename = "cg_allclasses.html";
   class_title = "Client Game Module";
#else
	class_filename = "cl_allclasses.html";
   class_title = "Client Module";
#endif

	class_file = fopen( class_filename.c_str(), "w" );
	if ( class_file == NULL )
		return;

   // construct the HTML header for the document
   CLASS_Print( class_file, "<HTML>\n");
   CLASS_Print( class_file, "<HEAD>\n");
   CLASS_Print( class_file, "<Title>%s Classes</Title>\n", class_title.c_str() );
   CLASS_Print( class_file, "</HEAD>\n");
   CLASS_Print( class_file, "<BODY>\n");
   CLASS_Print( class_file, "<H1>\n");
   CLASS_Print( class_file, "<center>%s Classes</center>\n", class_title.c_str() );
   CLASS_Print( class_file, "</H1>\n");
#if defined( GAME_DLL )
   //
   // print out some commonly used classnames
   //
   CLASS_Print( class_file, "<h2>" );
   CLASS_Print( class_file, "<a href=\"#Actor\">Actor</a>, " );
   CLASS_Print( class_file, "<a href=\"#Animate\">Animate</a>, " );
   CLASS_Print( class_file, "<a href=\"#Entity\">Entity</a>, " );
   CLASS_Print( class_file, "<a href=\"#ScriptSlave\">ScriptSlave</a>, " );
   CLASS_Print( class_file, "<a href=\"#ScriptThread\">ScriptThread</a>, " );
   CLASS_Print( class_file, "<a href=\"#Sentient\">Sentient</a>, " );
   CLASS_Print( class_file, "<a href=\"#Trigger\">Trigger</a>, " );
   CLASS_Print( class_file, "<a href=\"#World\">World</a>" );
   CLASS_Print( class_file, "</h2>" );
#endif

   dump_numclasses = 0;
   dump_numevents = 0;

   // get all the classes
   num = 0;
	for( c = classlist->next; c != classlist; c = c->next )
		{
      if ( num < MAX_CLASSES )
         {
         classes[ num++ ] = c->classname;
         }
		}

   // go through and process each class from smallest to greatest
   for( i = 0; i < num; i++ )
      {
      smallest = -1;
      for( j = 0; j < num; j++ )
         {
         if ( classes[ j ].length() > 1 )
            {
            if ( smallest >= 0 )
               {
               if ( classes[ j ].icmp( classes[ smallest ] ) < 0 )
                  {
                  smallest = j;
                  }
               }
            else
               {
               smallest = j;
               }
            }
         }
      DumpClass( class_file, classes[ smallest ], typeFlag );
      // delete the name from the list
      classes[ smallest ] = "";
      }

	if ( class_file != NULL )
		{
      CLASS_Print( class_file, "<H2>\n");
      CLASS_Print( class_file, "%d %s Classes.<BR>%d %s Events.\n", dump_numclasses, class_title.c_str(), dump_numevents, class_title.c_str() );
      CLASS_Print( class_file, "</H2>\n");
      CLASS_Print( class_file, "</BODY>\n");
      CLASS_Print( class_file, "</HTML>\n");
		CLASS_DPrintf( "Dumped all classes to file %s\n", class_filename.c_str() );
		fclose( class_file );
		}
*/
}


void ShutdownClasses( void )
{
	ClassDef *c;
	
	for( c = classlist->next; c != classlist; c = c->next )
	{
		c->Shutdown();
	}
}
