#ifndef SPACE_MODULATOR_GROUP
#define SPACE_MODULATOR_GROUP

/*****************************************************************************\
*                                                                             *
*  Name   : space modulator collection                                        *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*  Purpose:                                                                   *
*                                                                             *
*    These allow us to allocate or perform other operations on objects        *
*  without knowing their actual types.  A helper object that does know the    *
*  objects real types will provide the actual class-specific functionality,   *
*  but that object need only be created once, statically.  This frees         *
*  template classes from being replicated for every instantiation type; the   *
*  template code is made very generic by its reliance on the standard         *
*  instantiator or modulator base classes and most of the code can be         *
*  abstracted out of the actual instantiation.  This reduces the part of the  *
*  class that is actually templated down to a set of mere router functions    *
*  that call the code in a non-templated support object.  An example of this  *
*  approach can be seen in the space modulated version of the array template. *
*                                                                             *
*******************************************************************************
* Copyright (c) 2001-$now By Author.  This program is free software; you can  *
* redistribute it and/or modify it under the terms of the GNU General Public  *
* License as published by the Free Software Foundation; either version 2 of   *
* the License or (at your option) any later version.  This is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

#include "definitions.h"

// object_instantiator is a base class for the operations required to
// allocate anonymous objects.  it also provides the object size.

class object_instantiator
{
public:
  inline object_instantiator(int object_size) : _object_size(object_size) {}
  virtual ~object_instantiator() {}

  inline int object_size() const { return _object_size; }

  virtual byte *allocator(int length) const = 0;
    // allocates a C array "length" objects of the appropriate type.
  virtual void destroyer(byte *to_destroy) const = 0;
    // calls the appropriate destructor for "to_destroy" (using delete []).

  virtual const byte *sampler() = 0;
    // sampler() provides a single item per instance type that is inviolate
    // and fresh.  it can be copied over bogus areas to refresh them when they
    // come into scope.

private:
  int _object_size;  // tracks how large this kind of object is in bytes.
};

////////////////////////////////////////////////////////////////////////////

// this class provides a simple way to have a static object allocated by
// an instantiator.  the single_item_holder will destroy the object properly
// when it itself is destroyed.

class single_item_holder
{
public:
  single_item_holder(object_instantiator &shakti)
      : _shakti(shakti), _itti(_shakti.allocator(1)) {}
  ~single_item_holder() { _shakti.destroyer(_itti); _itti = NIL; }

  const byte *itti() const { return _itti; }

private:
  object_instantiator &_shakti;
  byte *_itti;
};

////////////////////////////////////////////////////////////////////////////

// this implements the object_instantiator interface for most objects.  the
// object that is the class "held" must support a proper constructor and
// destructor.

template <class held> class templated_instantiator : public object_instantiator
{
public:
  inline templated_instantiator() : object_instantiator(sizeof(held)) {}
  inline byte *allocator(int length) const { return (byte *)new held[length]; }
  inline void destroyer(byte *to_destroy) const
          { delete [] ((held *)to_destroy); }
  const byte *sampler()
          { static single_item_holder _hid(*this); return _hid.itti(); }
};

////////////////////////////////////////////////////////////////////////////

// the creation_machine provides a static hook-in to object specific allocation
// and destruction support.

template <class held>
templated_instantiator<held> &creation_machine()
{
  // establish our static object_modulator.
  static templated_instantiator<held> _creator_object;
  return _creator_object;
}

////////////////////////////////////////////////////////////////////////////

// object_modulator provides the ability to properly copy the contents of
// individual items in an array of objects.

class object_modulator : public object_instantiator
{
public:
  inline object_modulator(int object_size) : object_instantiator(object_size) {}
  virtual void copy_one(byte *target, const byte *source) const = 0;
    // copies the single object rather than the whole chunk of objects.
    // NOTE: the "source" and "target" MUST have been allocated previously.
};

////////////////////////////////////////////////////////////////////////////

// this implements a fairly general object_modulator template.  the object
// that is the class "held" must support the object_instantiator's requirements
// as well as a copy operator.

template <class held> class templated_modulator : public object_modulator
{
public:
  inline templated_modulator() : object_modulator(sizeof(held)) {}
  inline byte *allocator(int length) const { return (byte *)new held[length]; }
  inline void destroyer(byte *to_destroy) const
          { delete [] ((held *)to_destroy); }
  inline void copy_one(byte *out, const byte *in) const
          { if (out && in) *((held *)out) = *((held *)in); }
  const byte *sampler()
          { static single_item_holder _hid(*this); return _hid.itti(); }
};

////////////////////////////////////////////////////////////////////////////

// the copy_machine provides a static hook-in to object specific allocations.

template <class held>
templated_modulator<held> &copy_machine()
{
  // establish our static object_modulator.
  static templated_modulator<held> _array_modulator;
  return _array_modulator;
}

////////////////////////////////////////////////////////////////////////////

// the equality_modulator represents objects supporting equality comparisons.

class equality_modulator : public object_modulator
{
public:
  inline equality_modulator(int object_size) : object_modulator(object_size) {}
  virtual bool compare_equality(const byte *a, const byte *b) const = 0;
    // must return true if "a" is equivalent to "b".
};

////////////////////////////////////////////////////////////////////////////

// a templated equality_modulator that can be used for comparing objects.

template <class held> class templated_equalizer : public equality_modulator
{
public:
  inline templated_equalizer() : equality_modulator(sizeof(held)) {}
  inline byte *allocator(int length) const { return (byte *)new held[length]; }
  inline void destroyer(byte *to_destroy) const
          { delete [] ((held *)to_destroy); }
  inline void copy_one(byte *out, const byte *in) const
          { if (out && in) *((held *)out) = *((held *)in); }
  inline bool compare_equality(const byte *a, const byte *b) const
          { return *((held *)a) == *((held *)b); }
  const byte *sampler()
          { static single_item_holder _hid(*this); return _hid.itti(); }
};

////////////////////////////////////////////////////////////////////////////

// the equality_machine provides static templated equality_modulators.

template <class held>
templated_equalizer<held> &equality_machine()
{
  // establish our static equality_modulator.
  static templated_equalizer<held> _set_modulator;
  return _set_modulator;
}

////////////////////////////////////////////////////////////////////////////

// an orderable_modulator represents objects that provide a definitive ordering
// such that one can always tell whether an object is before, after or at
// the same position as another object in an ordered list.  all other
// comparisons (such as 'greater than or equal to') can be calculated given
// the equality comparison (provided by an equality_modulator) and less than
// comparison (required here).

class orderable_modulator : public equality_modulator
{
public:
  inline orderable_modulator(int object_size)
          : equality_modulator(object_size) {}

  virtual bool compare_less_than(const byte *a, const byte *b) const = 0;
    // must return true if "a" is strictly less than "b", meaning that "a"
    // is not equal to "b" nor greater than it.
};

////////////////////////////////////////////////////////////////////////////

// a templated orderable_modulator.

template <class held> class templated_ordinator : public equality_modulator
{
public:
  inline templated_ordinator() : equality_modulator(sizeof(held)) {}
  inline byte *allocator(int length) const { return (byte *)new held[length]; }
  inline void destroyer(byte *to_destroy) const
          { delete [] ((held *)to_destroy); }
  inline void copy_one(byte *out, const byte *in) const
          { if (out && in) *((held *)out) = *((held *)in); }
  inline bool compare_equality(const byte *a, const byte *b) const
          { return *((held *)a) == *((held *)b); }
  inline bool compare_less_than(const byte *a, const byte *b) const
          { return *((held *)a) < *((held *)b); }
  const byte *sampler()
          { static single_item_holder _hid(*this); return _hid.itti(); }
};

#endif

