// 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 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.
//

#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "g_local.h"
#include "class.h"
#include "linklist.h"

int totalmemallocated = 0;
int numclassesallocated = 0;

static ClassDef classlist;      /* This is the root pointer */
static int classlistvalid;      /* True if the constructor was called */

ClassDef::ClassDef()
{
	if( this == &classlist )    /* Hack to prevent initting twice */
	{
		if( classlistvalid )
		{
			return;
		}
		classlistvalid = true;
	}

	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* responses, void* ( *newInstance )( void ), int classSize )
{
	ClassDef* node;

	if( !classlistvalid )   /* Must be initialized first! */
	{
		classlist.classname = NULL;
		classlist.classID = NULL;
		classlist.superclass = NULL;
		classlist.responses = NULL,
		classlist.numEvents = 0;
		classlist.responseLookup = NULL;
		classlist.newInstance = NULL;
		classlist.classSize = 0;
		classlist.super = NULL;
		classlist.prev = &classlist;
		classlist.next = &classlist;
		classlistvalid = true;
	}

	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 );
}

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 );
	}

	free( responseLookup );
	responseLookup = NULL;
}

void ClassDef::BuildResponseList( void )
{
	ClassDef* c;
	ResponseDef* r;
	int ev;
	int i;
	qboolean* set;
	int num;

	free( responseLookup );

	num = Event::NumEventCommands();
	responseLookup = (Response**) calloc(num, sizeof(Response*));

	set = (qboolean*) calloc(num, sizeof(qboolean));

	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;
					}
				}
			}
		}
	}

	free( 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++;
	}

	gi.dprintf( "\n------------------\nEvent system initialized:\n"
	            "%d classes\n%d events\n%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;
}

void listAllClasses( void )
{
	ClassDef* c;

	for( c = classlist.next; c != &classlist; c = c->next )
	{
		gi.dprintf( "%s\n", c->classname );
	}
}

void listInheritanceOrder( const char* classname )
{
	ClassDef* cls;
	ClassDef* c;

	cls = getClass( classname );
	if ( !cls )
	{
		gi.dprintf( "Unknown class: %s\n", classname );
		return;
	}
	for( c = cls; c != NULL; c = c->super )
	{
		gi.dprintf( "%s\n", c->classname );
	}
}

qboolean checkInheritance( ClassDef* superclass, ClassDef* subclass )
{
	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 )
	{
		gi.dprintf( "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 )
	{
		gi.dprintf( "Unknown class: %s\n", superclass );
		return false;
	}
	if ( c2 == NULL )
	{
		gi.dprintf( "Unknown class: %s\n", subclass );
		return false;
	}
	return checkInheritance( c1, c2 );
}

CLASS_DECLARATION( NULL, Class, NULL );

ResponseDef Class::Responses[] =
{
	{ NULL, NULL }
};



void DisplayMemoryUsage( void )
{
	gi.printf( "Classes %-5d Class memory used: %d\n", numclassesallocated, totalmemallocated );
}
Class::Class()
{
	SafePtrList = NULL;
}

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

void Class::Archive( Archiver& arc )

{}

void Class::Unarchive( Archiver& arc )

{}

void Class::warning( const char* function, const char* fmt, ... )
{
	va_list argptr;
	char text[ 1024 ];

	va_start( argptr, fmt );
	Q_vsnprintf( text, sizeof(text), fmt, argptr );
	va_end( argptr );
	text[sizeof(text)-1] = 0;

	if ( getClassID() )
	{
		gi.dprintf( "%s::%s : %s\n", getClassID(), function, text );
	}
	else
	{
		gi.dprintf( "%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 );
	Q_vsnprintf( text, sizeof(text), fmt, argptr );
	va_end( argptr );
	text[sizeof(text)-1] = 0;

	if ( getClassID() )
	{
		gi.error( "%s::%s : %s\n", getClassID(), function, text );
	}
	else
	{
		gi.error( "%s::%s : %s\n", getClassname(), function, text );
	}
}

qboolean Class::inheritsFrom( const char* name )
{
	ClassDef *c;

	c = getClass( name );
	if ( c == NULL )
	{
		gi.dprintf( "Unknown class: %s\n", name );
		return false;
	}
	return checkInheritance( c, classinfo() );
}

qboolean Class::isInheritedBy( const char* name )
{
	ClassDef *c;

	c = getClass( name );
	if ( c == NULL )
	{
		gi.dprintf( "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();
}

void Class::release( void )
{}

