/*
** dobject.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2001 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/

#ifndef __DOBJECT_H__
#define __DOBJECT_H__

#include <stdlib.h>
#include "tarray.h"
#include "doomtype.h"

class FArchive;

class   DObject;
class           DArgs;
class           DBoundingBox;
class           DCanvas;
class           DConsoleCommand;
class                   DConsoleAlias;
class           DSeqNode;
class                   DSeqActorNode;
class                   DSeqPolyNode;
class                   DSeqSectorNode;
class           DThinker;
class                   AActor;
class                   DPolyAction;
class                           DMovePoly;
class                                   DPolyDoor;
class                           DRotatePoly;
class                   DPusher;
class                   DScroller;
class                   DSectorEffect;
class                           DLighting;
class                                   DFireFlicker;
class                                   DFlicker;
class                                   DGlow;
class                                   DGlow2;
class                                   DLightFlash;
class                                   DPhased;
class                                   DStrobe;
class                           DMover;
class                                   DElevator;
class                                   DMovingCeiling;
class                                           DCeiling;
class                                           DDoor;
class                                   DMovingFloor;
class                                           DFloor;
class                                           DFloorWaggle;
class                                           DPlat;
class                                   DPillar;

struct FActorInfo;

enum EMetaType
{
	META_Int,		// An int
	META_Fixed,		// A fixed point number
	META_String,	// A string
};

class FMetaData
{
private:
	FMetaData (EMetaType type, DWORD id) : Type(type), ID(id) {}

	FMetaData *Next;
	EMetaType Type;
	DWORD ID;
	union
	{
		int Int;
		char *String;
		fixed_t Fixed;
	} Value;

	friend class FMetaTable;
};

class FMetaTable
{
public:
	FMetaTable() : Meta(NULL) {}
	FMetaTable(const FMetaTable &other);
	~FMetaTable();
	FMetaTable &operator = (const FMetaTable &other);

	void SetMetaInt (DWORD id, int parm);
	void SetMetaFixed (DWORD id, fixed_t parm);
	void SetMetaString (DWORD id, const char *parm);	// The string is copied

	int GetMetaInt (DWORD id) const;
	fixed_t GetMetaFixed (DWORD id) const;
	const char *GetMetaString (DWORD id) const;

	FMetaData *FindMeta (EMetaType type, DWORD id) const;

private:
	FMetaData *Meta;
	FMetaData *FindMetaDef (EMetaType type, DWORD id);
	void FreeMeta ();
	void CopyMeta (const FMetaTable *other);
};

struct TypeInfo
{
#if !defined(_MSC_VER) && !defined(__GNUC__)
	TypeInfo () : Pointers (NULL) { RegisterType(); }
	TypeInfo (Dobject *DObject::*  *p, const char *inName, TypeInfo *inParentType, unsigned int inSize)
		: Name (inName),
		ParentType (inParentType),
		SizeOf (inSize),
		Pointers (p),
		TypeIndex (0),
		ActorInfo (NULL),
		HashNext (0)
	{ RegisterType(); }
	TypeInfo (DObject *Dobject::* const *p, const char *inName, TypeInfo *inParentType, unsigned int inSize, void (*inNew)(void *))
		: Name (inName),
		ParentType (inParentType),
		SizeOf (inSize),
		Pointers (p),
		ConstructNative (inNew),
		TypeIndex(0),
		ActorInfo (NULL),
		HashNext (0)
	{ RegisterType(); }
#else
	static void StaticInit ();
#endif
	const char *Name;
	TypeInfo *ParentType;
	unsigned int SizeOf;
	DObject *DObject::* const *Pointers;
	void (*ConstructNative)(void *);
	FActorInfo *ActorInfo;
	unsigned int HashNext;
	unsigned short TypeIndex;
	FMetaTable Meta;

	void RegisterType ();
	DObject *CreateNew () const;
	TypeInfo *CreateDerivedClass (char *name, unsigned int size);

	// Returns true if this type is an ancestor of (or same as) the passed type.
	bool IsAncestorOf (const TypeInfo *ti) const
	{
		while (ti)
		{
			if (this == ti)
				return true;
			ti = ti->ParentType;
		}
		return false;
	}
	inline bool IsDescendantOf (const TypeInfo *ti) const
	{
		return ti->IsAncestorOf (this);
	}

	static const TypeInfo *FindType (const char *name);
	static const TypeInfo *IFindType (const char *name);

	static unsigned short m_NumTypes, m_MaxTypes;
	static TypeInfo **m_Types;
	static TArray<TypeInfo *> m_RuntimeActors;

	enum { HASH_SIZE = 256 };
	static unsigned int TypeHash[HASH_SIZE];
};

#define RUNTIME_TYPE(object)    (object->GetClass())    // Passed an object, returns the type of that object
#define RUNTIME_CLASS(cls)              (&cls::_StaticType)             // Passed a class name, returns a TypeInfo representing that class
#define NATIVE_TYPE(object)             (object->StaticType())  // Passed an object, returns the type of the C++ class representing the object

enum EInPlace { EC_InPlace };

#define DECLARE_ABSTRACT_CLASS(cls,parent) \
public: \
	static TypeInfo _StaticType; \
	virtual TypeInfo *StaticType() const { return RUNTIME_CLASS(cls); } \
private: \
	typedef parent Super; \
	typedef cls ThisClass;

#define DECLARE_CLASS(cls,parent) \
	DECLARE_ABSTRACT_CLASS(cls,parent) \
		private: static void InPlaceConstructor (void *mem);

#define HAS_OBJECT_POINTERS \
	static DObject *ThisClass::* const _Pointers_[];

#define DECLARE_POINTER(field)  (DObject *ThisClass::* const)&ThisClass::field,
#define END_POINTERS			0 };

#if !defined(_MSC_VER) && !defined(__GNUC__)
#       define _IMP_TYPEINFO(cls,ptr,create) \
	TypeInfo cls::_StaticType (ptr, #cls, RUNTIME_CLASS(cls::Super), sizeof(cls), create);
#else

#       if defined(_MSC_VER)
#               pragma data_seg(".creg$u")
#               pragma data_seg()
#               define _DECLARE_TI(cls) __declspec(allocate(".creg$u")) TypeInfo *_##cls##AddType = &cls::_StaticType;
#       else
#               define _DECLARE_TI(cls) TypeInfo *_##cls##AddType __attribute__((section("creg"))) = &cls::_StaticType;
#       endif

#       define _IMP_TYPEINFO(cls,ptrs,create) \
	TypeInfo cls::_StaticType = { \
#cls, \
	RUNTIME_CLASS(cls::Super), \
	sizeof(cls), \
	ptrs, \
	create, }; \
	_DECLARE_TI(cls)

#endif

#define _IMP_CREATE_OBJ(cls) \
	void cls::InPlaceConstructor(void *mem) { new((EInPlace *)mem) cls; }

#define IMPLEMENT_POINTY_CLASS(cls) \
	_IMP_CREATE_OBJ(cls) \
	_IMP_TYPEINFO(cls,(DObject *DObject::* const *)cls::_Pointers_,cls::InPlaceConstructor) \
	DObject *cls::* const cls::_Pointers_[] = {

#define IMPLEMENT_CLASS(cls) \
	_IMP_CREATE_OBJ(cls) \
	_IMP_TYPEINFO(cls,NULL,cls::InPlaceConstructor) 

#define IMPLEMENT_ABSTRACT_CLASS(cls) \
	_IMP_TYPEINFO(cls,NULL,NULL)

enum EObjectFlags
{
	OF_MassDestruction	= 0x00000001,   // Object is queued for deletion
	OF_Cleanup			= 0x00000002,   // Object is being deconstructed as a result of a queued deletion
	OF_JustSpawned		= 0x00000004,   // Thinker was spawned this tic
	OF_SerialSuccess	= 0x10000000    // For debugging Serialize() calls
};

class DObject
{
public: \
	static TypeInfo _StaticType; \
	virtual TypeInfo *StaticType() const { return &_StaticType; } \
private: \
	typedef DObject ThisClass;

public:
	DObject ();
	DObject (TypeInfo *inClass);
	virtual ~DObject ();

	inline bool IsKindOf (const TypeInfo *base) const
	{
		return base->IsAncestorOf (GetClass ());
	}

	inline bool IsA (const TypeInfo *type) const
	{
		return (type == GetClass());
	}

	virtual void Serialize (FArchive &arc);

	// For catching Serialize functions in derived classes
	// that don't call their base class.
	void CheckIfSerialized () const;

	virtual void Destroy ();

	static void BeginFrame ();
	static void EndFrame ();

	// If you need to replace one object with another and want to
	// change any pointers from the old object to the new object,
	// use this method.
	static void PointerSubstitution (DObject *old, DObject *notOld);

	DWORD ObjectFlags;

	static void STACK_ARGS StaticShutdown ();

	TypeInfo *GetClass() const
	{
		if (Class == NULL)
		{
			// Save a little time the next time somebody wants this object's type
			// by recording it now.
			const_cast<DObject *>(this)->Class = StaticType();
		}
		return Class;
	}

	void SetClass (TypeInfo *inClass)
	{
		Class = inClass;
	}

	void *operator new(size_t len)
	{
		return Malloc(len);
	}

	void operator delete (void *mem)
	{
		free (mem);
	}

protected:
	// This form of placement new and delete is for use *only* by TypeInfo's
	// CreateNew() method. Do not use them for some other purpose.
	void *operator new(size_t len, EInPlace *mem)
	{
		return (void *)mem;
	}

	void operator delete (void *mem, EInPlace *foo)
	{
		free (mem);
	}

private:
	TypeInfo *Class;

	static TArray<DObject *> Objects;
	static TArray<size_t> FreeIndices;
	static TArray<DObject *> ToDestroy;

	static void DestroyScan (DObject *obj);
	static void DestroyScan ();

	void RemoveFromArray ();

	static bool Inactive;
	size_t Index;
};

#endif //__DOBJECT_H__
