/******** OBJECTS.H  Copyright 1992 Gregory Colvin *********
   This program may be distributed free with this notice.
***********************************************************/
#ifndef OBJECTS_H
#define OBJECTS_H
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#ifndef __cplusplus /* Not C++, assume C */

#if !defined(__MSDOS__) || defined(__STDC__)
  #define near      /* near pointers are DOS extension */
#endif

/* Base class for all objects: */
typedef struct {
  char near *name;
  int initialized;
  void (*destroy)(void);
  int (*isA)(const char *className);
} ObjectMethods;
extern ObjectMethods ObjectTable;
void ObjectInitClass(void);
typedef struct Object {
  ObjectMethods near *methods;
} Object;
void Object_construct(void);
void Object_destruct(void);

/* Manage "this" pointer stack for methods: */
extern Object * near *pThis;
#define MAX_PUSH 512
#if 1
#define PREV_THIS() (*(pThis-1))
#define PUSH_THIS(pObj) (*--pThis = (Object*)(pObj))
#define GET_THIS(Class) ((Class*)*pThis)
#define GET_THIS_SAFE(Class) \
  (assert(IS_A(*pThis,Class)),GET_THIS(Class))
#define POP_THIS() (*pThis++)
#define POP_THIS_SAFE(Class) \
  (assert(IS_A(*pThis,Class)),(Class*)POP_THIS())
#else
#define PREV_THIS() (*(Object**)_DI)
#define PUSH_THIS(pObj) ((*(Object**)_DI) = pObj)
#define GET_THIS(Class) (*(Class**)_DI)
#define GET_THIS_SAFE(Class) \
  (assert(IS_A(GET_THIS(Class),Class)),GET_THIS(Class))
#define POP_THIS() (*(Object**)_DI)
#define POP_THIS_SAFE(Class) GET_THIS_SAFE(Class)
#endif

/* Begin class methods declaration: */
#define DCL_METHODS(Class,Base,ConstructorArguments) \
 typedef struct Class##DataStruct Class;             \
 void Class##_construct ConstructorArguments;        \
 void Class##_destruct(void);                        \
 struct Class##MethodStruct {                        \
  Base##Methods base;

/* Declare and redeclare methods: */
#define DCL_METHOD(Name,Arguments,Return) \
 Return (*Name)Arguments
#define DCL_ABSTRACT(Name,Arguments,Return) \
 Return (*Name)Arguments
#define REDCL_METHOD(Name,Arguments,Return) \
 void dummy_##Name

/* End class methods or class type declaration: */
#define END_METHODS };

/* Begin class members declaration: */
#define DCL_MEMBERS(Class,Base)                     \
 typedef struct Class##MethodStruct Class##Methods; \
 extern Class##Methods Class##Table;                \
 extern void Class##InitClass(void);                \
 struct Class##DataStruct {                         \
  Base base;

#define END_MEMBERS };

/* Begin class type definition: */
#define DEF_CLASS(Class,Base)                              \
 Class##Methods Class##Table = { 0, 0 };                   \
 int Class##_isA(const char *className) {                  \
  int yes= !strcmp(#Class,className);                      \
  if (!yes)                                                \
   yes = ((ObjectMethods*)(&Base##Table))->isA(className); \
  return yes;                                              \
 }                                                         \
 void Class##_destroy(void) {                              \
  Class##_destruct();                                      \
  ((ObjectMethods*)(&Base##Table))->destroy();             \
 }                                                         \
 void Class##InitClass(void) {                             \
  ObjectMethods *objTable= (ObjectMethods*)&Class##Table;  \
  Base##Methods *baseTable= (Base##Methods*)&Class##Table; \
  Base##InitClass();                                       \
  if (objTable->initialized)                               \
   return;                                                 \
  *baseTable = Base##Table;                                \
  objTable->name = #Class;                                 \
  REDEF_METHOD(Class,Object,isA);                          \
  REDEF_METHOD(Class,Object,destroy);

/* Set and reset methods within a class definition: */
#define DEF_ABSTRACT(Base,Method) \
 (Base##Table.Method = 0)
#define DEF_METHOD(Base,Method) \
 (Base##Table.Method = Base##_##Method)
#define REDEF_METHOD(Class,Base,Method)       \
 (((Base##Methods *)(&Class##Table))->Method  \
  = Class##_##Method)

/* End class type definition: */
#define END_CLASS }

/* Initialize a class before use: */
#define USE(Class) Class##InitClass()

/* Begin class method implementation: */
#define METHOD(Class,Name,Arguments,Return) \
 Return Class##_##Name Arguments {          \
  Class *this = POP_THIS_SAFE(Class);

/* End class method implementation: */
#define END_METHOD }

/* Begin class constructor implementation: */
#define CONSTRUCTOR(Class,Arguments) \
 void Class##_construct Arguments {  \
  Class *this= GET_THIS(Class);      \
  ObjectMethods *Methods=            \
   (ObjectMethods *)&Class##Table;

/* Must construct Base first in class constructor: */
#define CONSTRUCT(Base,Arguments)       \
 Base##_construct Arguments;            \
 ((Object *)this)->methods = Methods; {

/* End class constructor implementation: */
#define END_CONSTRUCTOR }}

/* Begin class destructor implementation: */
#define DESTRUCTOR(Class)             \
 void Class##_destruct(void) {        \
  Class *this = GET_THIS_SAFE(Class); \
  ((Object *)this)->methods =         \
   (ObjectMethods *)&Class##Table; {

/* End class destructor implementation: */
#define END_DESTRUCTOR }}

/* Monomorphic access to a method of a class: */
#define CALL(pObj,Class,Base,Name,Arguments)         \
 ((PUSH_THIS(pObj),                                  \
  (Base##Methods *)(&Class##Table))->Name Arguments  \
 )

/* Polymorphic access to a method of a class: */
#define SEND(pObj,Base,Name,Arguments)         \
 (((Base##Methods*)                            \
  (PUSH_THIS(pObj)->methods))->Name Arguments)

/* Construct a new object on the heap: */
#define NEW(Class,Arguments)          \
 (PUSH_THIS(malloc(sizeof(Class)))),  \
  Class##_construct Arguments,        \
  POP_THIS_SAFE(Class)))

/* Construct a new object on the stack: */
#define PUSH(ObjName,Class,Arguments) \
 Class auto_##ObjName;                \
 Class *ObjName = (Class*)            \
  (PUSH_THIS(&auto_##ObjName),        \
   Class##_construct Arguments,       \
   POP_THIS_SAFE(Class))

/* Destroy an object on the heap: */
#define DELETE(pObj) \
 (SEND(pObj,Object,destroy,()), free(POP_THIS())

/* Destroy an object on the stack: */
#define POP(pObj) SEND(pObj,Object,destroy, ())

/* Test whether object is a member of named class: */
#define IS_A(pObj,Class) \
 ((((Object *)pObj)->methods)->isA(#Class))

/* Access to the class name of an object: */
#define CLASS_NAME(pObj) \
 (((ObjectTable*)(((Object *)(pObj))->methods))->name)

#else /* C++ */

/* Base class for all objects: */
struct Object {
  virtual int isA(const char *className);
  virtual const char *classname();
};

/* Begin class methods declaration: */
#define DCL_METHODS(Class,Base,ConstructorArguments) \
 struct Class : Base {                               \
    Class ConstructorArguments;                      \
    virtual ~Class();                                \
    virtual int isA(const char *className);          \
    virtual const char *classname();

/* Declare and redeclare methods: */
#define DCL_METHOD(Name,Arguments,Return) \
 virtual Return Name Arguments
#define DCL_ABSTRACT(Name,Arguments,Return) \
 virtual Return Name Arguments=0
#define REDCL_METHOD(Name,Arguments,Return) \
 virtual Return Name Arguments

/* End class methods declaration: */
#define END_METHODS

/* Begin class members declaration: */
#define DCL_MEMBERS(Class,Base)

/* End class type declaration: */
#define END_MEMBERS };

/* Begin class type definition: */
#define DEF_CLASS(Class,Base)                       \
 const char *Class::classname() {                   \
  return #Class;                                    \
 }                                                  \
 int Class::isA(const char *className) {            \
  if (strcmp(#Class,className))                     \
   return Base::isA(#Class);                        \
  return 1;                                         \
 }                                                  \

/* Set and reset methods within a class definition: */
#define DEF_ABSTRACT(Base,Method)
#define DEF_METHOD(Base,Method)
#define REDEF_METHOD(Class,Base,Method)

/* End class type definition: */
#define END_CLASS

/* Initialize a class before use: */
#define USE(Class)

/* Begin class method implementation: */
#define METHOD(Class,Name,Arguments,Return) \
 Return Class::Name Arguments {

/* End class method implementation: */
#define END_METHOD }

/* Begin class constructor implementation: */
#define CONSTRUCTOR(Class,Arguments) Class::Class Arguments:

/* Must construct Base first in class constructor: */
#define CONSTRUCT(Base,Arguments) Base Arguments {

/* End class constructor implementation: */
#define END_CONSTRUCTOR }

/* Begin class desstructor implementation: */
#define DESTRUCTOR(Class) Class::~Class() {

/* End class destructor implementation: */
#define END_DESTRUCTOR }

/* Monomorphic access to a method of a class: */
#define CALL(pObj,Class,Base,Name,Arguments) \
 (pObj->Class::Name Arguments)

/* Polymorphic access to a method of a class: */
#define SEND(pObj,Base,Name,Arguments) \
 (pObj->Name Arguments)

/* Construct a new object on the heap: */
#define CREATE(Class) new Class

/* Construct a new object on the stack: */
#define PUSH(ObjName,Class,Arguments) \
 Class auto##ObjName Arguments;       \
 Class *ObjName= &auto##ObjName

/* Destroy an object on the heap: */
#define DELETE(pObj) \
 delete(pObj)

/* Destroy an object on the stack: */
#define POP(pObj)

/* Test whether object is a member of named class: */
#define IS_A(pObj,className) \
 SEND(pObj,Object,isA,(className))

/* Access to the class name of an object: */
#define CLASS_NAME(pObj) (pObj->classname())

#endif /* C vs. C++ */

#endif
