#ifndef __oop_h

#include <stdlib.h>
#include <stdarg.h>

#define OOP_USE_GC	1

enum {
  InitialCollectionSize= 2
};

typedef struct Object *oop;


#define className(name)	struct name;
#include "oop-classes.h"
#undef className


#define subclassResponsibility(value)							\
  {											\
    error("'%s' should have overridden '%s'", _debugName(), __PRETTY_FUNCTION__);	\
    abort();										\
    return value;									\
  }

#define shouldNotImplement(value)							\
  {											\
    error("'%s' should not implement '%s'", _debugName(), __PRETTY_FUNCTION__);		\
    abort();										\
    return value;									\
  }


#if (OOP_USE_GC)
# include <gc/gc_cpp.h>
#else
# define GC_INIT()		fprintf(stderr, "WARNING: GC DISABLED\n")
# define GC_MALLOC(SIZE)	malloc(SIZE)
# define GC_MALLOC_ATOMIC(SIZE)	malloc(SIZE)
# define GC_FREE(POINTER)	free(POINTER)
  struct gc_cleanup { virtual ~gc_cleanup(void) {} };
#endif

struct Object : gc_cleanup
{
  virtual char *_debugName(void)= 0;
  virtual oop    error(char *fmt, ...);

# define method(type, name, args, value) virtual type name args;
# include "oop-methods.h"
# undef method

  virtual unsigned hash(void);
  virtual int operator ==(oop anObject);
  virtual int operator !=(oop anObject);

  unsigned identityHash(void);

# define className(name)	virtual int is##name(void);
# include "oop-classes.h"
# undef className

  virtual oop speciesNew(int n);

  virtual oop   printString(void);
  virtual char *printCString(void);
  virtual oop   printOn(oop aStream);
};


#define className(name)					\
  virtual char *_debugName(void) { return #name; }	\
  virtual int   is##name(void) { return 1; }


struct Magnitude : Object
{
  className(Magnitude);

  int operator < (oop aMagnitude);
  int operator <=(oop aMagnitude);
  int operator ==(oop aMagnitude);
  int operator > (oop aMagnitude);
  int operator >=(oop aMagnitude);
  int between(oop min, oop max);
  unsigned hash(void);
  oop max(oop aMagnitude);
  oop min(oop aMagnitude);
  oop minMax(oop aMin, oop aMax);
};


struct Character : Magnitude
{
  className(Character);

  static oop Table;
  static oop Tab, NL, CR, Space;

  static oop makeTable(void);

  int	mValue;

  Character(int value);

  static oop value(int value);
  static oop asciiValue(int value);
  static oop digitValue(int value);

  virtual int asciiValue(void);
  virtual int digitValue(void);
  virtual unsigned hash(void);
  virtual int operator <(oop anObject);
  virtual int operator ==(oop anObject);
  virtual int operator >(oop anObject);
  virtual int asInteger(void);
};


struct Association : Object
{
  className(Association);

  oop	mKey;
  oop	mValue;

  Association(oop key, oop value);

  virtual oop key(void);
  virtual oop value(void);
  virtual oop key(oop key);
  virtual oop value(oop value);

  virtual unsigned hash(void);
  virtual int operator ==(oop anObject);

  virtual oop printOn(oop aStream);
};


struct Collection : Object
{
  className(Collection);

  virtual oop classNew(int size);

  virtual oop errorEmptyCollection(void);
  virtual oop errorSubscriptBounds(int index);
  virtual oop emptyCheck(void);
  virtual int isEmpty(void);
  virtual int notEmpty(void);
  virtual oop asArray(void);
  virtual oop addAll(oop aCollection);
  virtual int includes(oop anObject);
  virtual oop asSortedCollection(void);
  virtual oop asSortedCollection(int (*block)(oop x, oop y));
  virtual oop printOn(oop aStream);
};


struct Set : Collection
{
  className(Set);

  int	mTally;
  oop	mArray;

  Set(int size= InitialCollectionSize);

  virtual oop classNew(int size);

  virtual int scanFor(oop anObject);
  virtual int findElementOrNil(oop anObject);
  virtual int growSize(void);
  virtual oop grow(void);
  virtual oop fullCheck(void);
  virtual oop noCheckAdd(oop anObject);
  virtual oop atNewIndexPut(int index, oop anObject);

  virtual oop add(oop element);
  virtual int size(void);
  virtual int includes(oop anObject);
  virtual oop like(oop anObject);

  virtual oop iterator(void);
  virtual oop array(void);
};


struct IdentitySet : Set
{
  IdentitySet(int size= InitialCollectionSize);

  virtual oop classNew(int size);

  virtual int scanFor(oop anObject);
};


struct Dictionary : Set
{
  className(Dictionary);

  Dictionary(int size= InitialCollectionSize);

  virtual oop classNew(int size);

  virtual oop noCheckAdd(oop anObject);
  virtual oop at(oop key);
  virtual oop atOrNil(oop key);
  virtual oop atPut(oop key, oop value);
  virtual oop keys(void);
  virtual int includesKey(oop key);
};


struct IdentityDictionary : Dictionary
{
  className(IdentityDictionary);

  IdentityDictionary(int size= InitialCollectionSize);

  virtual oop classNew(int size);

  virtual int scanFor(oop anObject);
  virtual oop keys(void);
};


struct SequenceableCollection : Collection
{
  className(SequenceableCollection);

  virtual oop atAllPut(oop value);
  virtual oop replaceFromToWithStartingAt(int from, int to, oop aCollection, int start);
  virtual oop copyFromTo(int from, int to);
  virtual oop readStream(void);
  virtual oop writeStream(void);
  virtual oop first(void);
  virtual oop last(void);
  virtual int operator ==(oop anObject);
};


struct OrderedCollection : SequenceableCollection
{
  className(OrderedCollection);

  oop	mArray;
  int	mFirstIndex;
  int	mLastIndex;

  OrderedCollection(int size= InitialCollectionSize);

  virtual oop classNew(int size);

  virtual oop reset(void);
  virtual oop setCollection(oop anArray);

  virtual int growSize(void);
  virtual oop makeRoomAtFirst(void);
  virtual oop makeRoomAtLast(void);

  virtual oop addFirst(oop newObject);
  virtual oop addLast(oop newObject);
  virtual oop add(oop newObject);

  virtual oop removeFirst(void);
  virtual oop removeLast(void);

  virtual oop at(int index);
  virtual oop atPut(int index, oop aCharacter);

  virtual oop insertBefore(oop anObject, int spotIndex);

  virtual int size(void);

  virtual oop iterator(void);
};


struct SortedCollection : OrderedCollection
{
  className(SortedCollection);

  int (*mSortBlock)(oop x, oop y);

  SortedCollection(int size= InitialCollectionSize);

  virtual oop sortBlock(int (*compare)(oop x, oop y));

  virtual oop addFirst(oop anObject);
  virtual oop addLast(oop anObject);
  virtual oop insertBefore(oop anObject, int index);

  virtual oop add(oop newObject);

  virtual int indexForInserting(oop newObject);
};


struct ArrayedCollection : SequenceableCollection
{
  className(ArrayedCollection);

  int	mSize;

  ArrayedCollection(int size);

  virtual int size(void);

  virtual oop iterator(void);
};


struct Array : ArrayedCollection
{
  className(Array);

  oop	*mElements;

  Array(int size);
  ~Array(void);

  virtual oop classNew(int size);

  virtual oop	at(int index);
  virtual oop	atPut(int index, oop element);
};


struct ByteArray : ArrayedCollection
{
  className(ByteArray);

  unsigned char	*mElements;

  ByteArray(int size);
  ~ByteArray(void);

  virtual oop classNew(int size);

  virtual int byteAt(int index);
  virtual int byteAtPut(int index, int byte);
};


struct String : ByteArray
{
  className(String);

  String(int size= 0);
  String(char *string);

  virtual oop classNew(int size);

  virtual unsigned hash(void);

  virtual int compare(oop anObject);

  virtual int operator < (oop anObject);
  virtual int operator <=(oop anObject);
  virtual int operator ==(oop anObject);
  virtual int operator >=(oop anObject);
  virtual int operator > (oop anObject);

  virtual oop operator +(oop anObject);

  virtual oop at(int index);
  virtual oop atPut(int index, oop aCharacter);

  virtual int size(void);
  virtual char *cString(void);

  virtual oop printOn(oop aStream);
};


struct Symbol : String
{
  className(Symbol);

  static oop Table;

  Symbol(char *bytes);

  static oop intern(oop anStringOrSymbol);
};


struct Stream : Object
{
  className(Stream);

  virtual oop nextPut(oop anObject);
  virtual oop nextPutAll(oop aCollection);

  virtual oop contents(void);
};


struct PositionableStream : Stream
{
  className(PositionableStream);

  oop	mCollection;
  int	mPosition;
  int	mReadLimit;

  virtual oop on(oop aCollection);
  virtual oop reset(void);
  virtual int atEnd(void);

  virtual oop contents(void);
};


struct ReadStream : PositionableStream
{
  className(ReadStream);

  ReadStream(oop aCollection);

  oop next(void);
};


struct WriteStream : PositionableStream
{
  className(WriteStream);

  int	mWriteLimit;

  WriteStream(oop aCollection);

  virtual oop on(oop aCollection);
  virtual oop reset(void);

  virtual oop nextPut(oop anObject);
  virtual oop pastEndPut(oop anObject);
  virtual oop tab(int n);
  virtual oop tab(void);
  virtual oop nl(int n);
  virtual oop nl(void);
  virtual oop cr(int n);
  virtual oop cr(void);
  virtual oop space(int n);
  virtual oop space(void);
  virtual oop print(oop anObject);
  virtual oop vformat(char *fmt, va_list ap);
  virtual oop format(char *fmt, ...);

  virtual oop contents(void);
};


struct Iterator : Object
{
  className(Iterator);

  oop mIterator;

  Iterator(oop anObject);
  ~Iterator(void);

  virtual int atEnd(void);
  virtual oop element(void);
  virtual oop step(void);

  virtual oop operator *(void);
};

#define iterate(collection, iterator)		\
  for (Iterator iterator(collection);		\
       !iterator.atEnd();			\
       iterator.step())

#define iterateWith(collection1, collection2, iterator1, iterator2)	\
  for (Iterator iterator1(collection1), iterator2(collection2);		\
       !iterator1.atEnd() && !iterator2.atEnd();			\
       iterator1.step(), iterator2.step())


struct ArrayIterator : Object
{
  className(ArrayIterator);

  oop mArray;
  int mIndex;
  int mLimit;

  ArrayIterator(oop array);

  virtual int atEnd(void);
  virtual oop element(void);
  virtual oop step(void);
};


struct SetIterator : ArrayIterator
{
  className(SetIterator);

  oop mElement;

  SetIterator(oop set);

  virtual int atEnd(void);
  virtual oop element(void);
  virtual oop step(void);
};


#define __oop_h
#endif // __oop_h

