//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef _SIMBASE_H_
#define _SIMBASE_H_

#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _TALGORITHM_H_
#include "core/tAlgorithm.h"
#endif
#ifndef _BITSET_H_
#include "core/bitSet.h"
#endif

#ifndef _CONSOLEOBJECT_H_
#include "console/consoleObject.h"
#endif
#ifndef _SIMDICTIONARY_H_
#include "console/simDictionary.h"
#endif

#ifndef _PLATFORMMUTEX_H_
#include "platform/platformMutex.h"
#endif

#ifndef _PLATFORMSEMAPHORE_H_
#include "platform/platformSemaphore.h"
#endif

//---------------------------------------------------------------------------

/// Definition of some basic Sim system constants.
///
/// These constants define the range of ids assigned to datablocks
/// (DataBlockObjectIdFirst - DataBlockObjectIdLast), and the number
/// of bits used to store datablock IDs.
///
/// Normal Sim objects are given the range of IDs starting at
/// DynamicObjectIdFirst and going to infinity. Sim objects use
/// a SimObjectId to represent their ID; this is currently a U32.
///
/// The RootGroupId is assigned to gRootGroup, in which most SimObjects
/// are addded as child members. See simManager.cc for details, particularly
/// Sim::initRoot() and following.
enum SimObjectsConstants
{
   DataBlockObjectIdFirst = 3,
   DataBlockObjectIdBitSize = 10,
   DataBlockObjectIdLast = DataBlockObjectIdFirst + (1 << DataBlockObjectIdBitSize) - 1,

   DynamicObjectIdFirst = DataBlockObjectIdLast + 1,
   InvalidEventId = 0,
   RootGroupId = 0xFFFFFFFF,
};

class SimEvent;
class SimObject;
class SimGroup;
class SimManager;
class Namespace;
class BitStream;
class Stream;
class LightManager;

typedef U32 SimTime;
typedef U32 SimObjectId;

/// A vector of SimObjects.
///
/// As this inherits from VectorPtr, it has the full range of vector methods.
class SimObjectList: public VectorPtr<SimObject*>
{
   static S32 QSORT_CALLBACK compareId(const void* a,const void* b);
  public:
   void pushBack(SimObject*);       ///< Add the SimObject* to the end of the list, unless it's already in the list.
   void pushBackForce(SimObject*);  ///< Add the SimObject* to the end of the list, moving it there if it's already present in the list.
   void pushFront(SimObject*);      ///< Add the SimObject* to the start of the list.
   void remove(SimObject*);         ///< Remove the SimObject* from the list; may disrupt order of the list.

   /// Remove the SimObject* from the list; guaranteed to preserve list order.
   void removeStable(SimObject* pObject);

   void sortId();                   ///< Sort the list by object ID.
};

//---------------------------------------------------------------------------

/// Represents a queued event in the sim.
///
/// Sim provides an event queue for your convenience, which
/// can be used to schedule events. A few things which use
/// this event queue:
///
///     - The scene lighting system. In order to keep the game
///       responsive while scene lighting occurs, the lighting
///       process is divided into little chunks. In implementation
///       terms, there is a subclass of SimEvent called
///       SceneLightingProcessEvent. The process method of this
///       subclass calls into the lighting code, telling it to
///       perform the next chunk of lighting calculations.
///     - The schedule() console function uses a subclass of
///       SimEvent called SimConsoleEvent to keep track of
///       scheduled events.
class SimEvent
{
  public:
   SimEvent *nextEvent;     ///< Linked list details - pointer to next item in the list.
   SimTime startTime;       ///< When the event was posted.
   SimTime time;            ///< When the event is scheduled to occur.
   U32 sequenceCount;       ///< Unique ID. These are assigned sequentially based on order
                            ///  of addition to the list.
   SimObject *destObject;   ///< Object on which this event will be applied.

   SimEvent() { destObject = NULL; }
   virtual ~SimEvent() {}   ///< Destructor
                            ///
                            /// A dummy virtual destructor is required
                            /// so that subclasses can be deleted properly

   /// Function called when event occurs.
   ///
   /// This is where the meat of your event's implementation goes.
   ///
   /// See any of the subclasses for ideas of what goes in here.
   ///
   /// The event is deleted immediately after processing. If the
   /// object referenced in destObject is deleted, then the event
   /// is not called. The even will be executed unconditionally if
   /// the object referenced is NULL.
   ///
   /// @param   object  Object stored in destObject.
   virtual void process(SimObject *object)=0;
};

/// Implementation of schedule() function.
///
/// This allows you to set a console function to be
/// called at some point in the future.
class SimConsoleEvent : public SimEvent
{
protected:
   S32 mArgc;
   char **mArgv;
   bool mOnObject;
  public:

   /// Constructor
   ///
   /// Pass the arguments of a function call, optionally on an object.
   ///
   /// The object for the call to be executed on is specified by setting
   /// onObject and storing a reference to the object in destObject. If
   /// onObject is false, you don't need to store anything into destObject.
   ///
   /// The parameters here are passed unmodified to Con::execute() at the
   /// time of the event.
   ///
   /// @see Con::execute(S32 argc, const char *argv[])
   /// @see Con::execute(SimObject *object, S32 argc, const char *argv[])
   SimConsoleEvent(S32 argc, const char **argv, bool onObject);

   ~SimConsoleEvent();
   virtual void process(SimObject *object);
};

/// Used by Con::threadSafeExecute()
struct SimConsoleThreadExecCallback
{
   void *sem;
   const char *retVal;

   SimConsoleThreadExecCallback();
   ~SimConsoleThreadExecCallback();

   void handleCallback(const char *ret);
   const char *waitForResult();
};

class SimConsoleThreadExecEvent : public SimConsoleEvent
{
   SimConsoleThreadExecCallback *cb;

public:
   SimConsoleThreadExecEvent(S32 argc, const char **argv, bool onObject, SimConsoleThreadExecCallback *callback);

   virtual void process(SimObject *object);
};


//---------------------------------------------------------------------------
/// Dictionary to keep track of dynamic fields on SimObject.
class SimFieldDictionary
{
   friend class SimFieldDictionaryIterator;

  public:
   struct Entry
   {
      StringTableEntry slotName;
      char *value;
      Entry *next;
   };
  private:
   enum
   {
      HashTableSize = 19
   };
   Entry *mHashTable[HashTableSize];

   static Entry *mFreeList;
   static void freeEntry(Entry *entry);
   static Entry *allocEntry();

   /// In order to efficiently detect when a dynamic field has been
   /// added or deleted, we increment this every time we add or
   /// remove a field.
   U32 mVersion;

public:
   const U32 getVersion() const { return mVersion; }

   SimFieldDictionary();
   ~SimFieldDictionary();
   void setFieldValue(StringTableEntry slotName, const char *value);
   const char *getFieldValue(StringTableEntry slotName);
   void writeFields(SimObject *obj, Stream &strem, U32 tabStop);
   void printFields(SimObject *obj);
   void assignFrom(SimFieldDictionary *dict);
};

class SimFieldDictionaryIterator
{
   SimFieldDictionary *          mDictionary;
   S32                           mHashIndex;
   SimFieldDictionary::Entry *   mEntry;

  public:
   SimFieldDictionaryIterator(SimFieldDictionary*);
   SimFieldDictionary::Entry* operator++();
   SimFieldDictionary::Entry* operator*();
};

//---------------------------------------------------------------------------
/// Base class for objects involved in the simulation.
///
/// @section simobject_intro Introduction
///
/// SimObject is a base class for most of the classes you'll encounter
/// working in Torque. It provides fundamental services allowing "smart"
/// object referencing, creation, destruction, organization, and location.
/// Along with SimEvent, it gives you a flexible event-scheduling system,
/// as well as laying the foundation for the in-game editors, GUI system,
/// and other vital subsystems.
///
/// @section simobject_subclassing Subclassing
///
/// You will spend a lot of your time in Torque subclassing, or working
/// with subclasses of, SimObject. SimObject is designed to be easy to
/// subclass.
///
/// You should not need to override anything in a subclass except:
///     - The constructor/destructor.
///     - processArguments()
///     - onAdd()/onRemove()
///     - onGroupAdd()/onGroupRemove()
///     - onNameChange()
///     - onStaticModified()
///     - onDeleteNotify()
///     - onEditorEnable()/onEditorDisable()
///     - inspectPreApply()/inspectPostApply()
///     - things from ConsoleObject (see ConsoleObject docs for specifics)
///
/// Of course, if you know what you're doing, go nuts! But in most cases, you
/// shouldn't need to touch things not on that list.
///
/// When you subclass, you should define a typedef in the class, called Parent,
/// that references the class you're inheriting from.
///
/// @code
/// class mySubClass : public SimObject {
///     typedef SimObject Parent;
///     ...
/// @endcode
///
/// Then, when you override a method, put in:
///
/// @code
/// bool mySubClass::onAdd()
/// {
///     if(!Parent::onAdd())
///         return false;
///
///     // ... do other things ...
/// }
/// @endcode
///
/// Of course, you want to replace onAdd with the appropriate method call.
///
/// @section simobject_lifecycle A SimObject's Life Cycle
///
/// SimObjects do not live apart. One of the primary benefits of using a
/// SimObject is that you can uniquely identify it and easily find it (using
/// its ID). Torque does this by keeping a global hierarchy of SimGroups -
/// a tree - containing every registered SimObject. You can then query
/// for a given object using Sim::findObject() (or SimSet::findObject() if
/// you want to search only a specific set).
///
/// @code
///        // Three examples of registering an object.
///
///        // Method 1:
///        AIClient *aiPlayer = new AIClient();
///        aiPlayer->registerObject();
///
///        // Method 2:
///        ActionMap* globalMap = new ActionMap;
///        globalMap->registerObject("GlobalActionMap");
///
///        // Method 3:
///        bool reg = mObj->registerObject(id);
/// @endcode
///
/// Registering a SimObject performs these tasks:
///     - Marks the object as not cleared and not removed.
///     - Assigns the object a unique SimObjectID if it does not have one already.
///     - Adds the object to the global name and ID dictionaries so it can be found
///       again.
///     - Calls the object's onAdd() method. <b>Note:</b> SimObject::onAdd() performs
///       some important initialization steps. See @ref simobject_subclassing "here
///       for details" on how to properly subclass SimObject.
///     - If onAdd() fails (returns false), it calls unregisterObject().
///     - Checks to make sure that the SimObject was properly initialized (and asserts
///       if not).
///
/// Calling registerObject() and passing an ID or a name will cause the object to be
/// assigned that name and/or ID before it is registered.
///
/// Congratulations, you have now registered your object! What now?
///
/// Well, hopefully, the SimObject will have a long, useful life. But eventually,
/// it must die.
///
/// There are a two ways a SimObject can die.
///         - First, the game can be shut down. This causes the root SimGroup
///           to be unregistered and deleted. When a SimGroup is unregistered,
///           it unregisters all of its member SimObjects; this results in everything
///           that has been registered with Sim being unregistered, as everything
///           registered with Sim is in the root group.
///         - Second, you can manually kill it off, either by calling unregisterObject()
///           or by calling deleteObject().
///
/// When you unregister a SimObject, the following tasks are performed:
///     - The object is flagged as removed.
///     - Notifications are cleaned up.
///     - If the object is in a group, then it removes itself from the group.
///     - Delete notifications are sent out.
///     - Finally, the object removes itself from the Sim globals, and tells
///       Sim to get rid of any pending events for it.
///
/// If you call deleteObject(), all of the above tasks are performed, in addition
/// to some sanity checking to make sure the object was previously added properly,
/// and isn't in the process of being deleted. After the object is unregistered, it
/// deallocates itself.
///
/// @section simobject_editor Torque Editors
///
/// SimObjects are one of the building blocks for the in-game editors. They
/// provide a basic interface for the editor to be able to list the fields
/// of the object, update them safely and reliably, and inform the object
/// things have changed.
///
/// This interface is implemented in the following areas:
///     - onNameChange() is called when the object is renamed.
///     - onStaticModified() is called whenever a static field is modified.
///     - inspectPreApply() is called before the object's fields are updated,
///                     when changes are being applied.
///     - inspectPostApply() is called after the object's fields are updated.
///     - onEditorEnable() is called whenever an editor is enabled (for instance,
///                     when you hit F11 to bring up the world editor).
///     - onEditorDisable() is called whenever the editor is disabled (for instance,
///                     when you hit F11 again to close the world editor).
///
/// (Note: you can check the variable gEditingMission to see if the mission editor
/// is running; if so, you may want to render special indicators. For instance, the
/// fxFoliageReplicator renders inner and outer radii when the mission editor is
/// runnning.)
///
/// @section simobject_console The Console
///
/// SimObject extends ConsoleObject by allowing you to
/// to set arbitrary dynamic fields on the object, as well as
/// statically defined fields. This is done through two methods,
/// setDataField and getDataField, which deal with the complexities of
/// allowing access to two different types of object fields.
///
/// Static fields take priority over dynamic fields. This is to be
/// expected, as the role of dynamic fields is to allow data to be
/// stored in addition to the predefined fields.
///
/// The fields in a SimObject are like properties (or fields) in a class.
///
/// Some fields may be arrays, which is what the array parameter is for; if it's non-null,
/// then it is parsed with dAtoI and used as an index into the array. If you access something
/// as an array which isn't, then you get an empty string.
///
/// <b>You don't need to read any further than this.</b> Right now,
/// set/getDataField are called a total of 6 times through the entire
/// Torque codebase. Therefore, you probably don't need to be familiar
/// with the details of accessing them. You may want to look at Con::setData
/// instead. Most of the time you will probably be accessing fields directly,
/// or using the scripting language, which in either case means you don't
/// need to do anything special.
///
/// The functions to get/set these fields are very straightforward:
///
/// @code
///  setDataField(StringTable->insert("locked", false), NULL, b ? "true" : "false" );
///  curObject->setDataField(curField, curFieldArray, STR.getStringValue());
///  setDataField(slotName, array, value);
/// @endcode
///
/// <i>For advanced users:</i> There are two flags which control the behavior
/// of these functions. The first is ModStaticFields, which controls whether
/// or not the DataField functions look through the static fields (defined
/// with addField; see ConsoleObject for details) of the class. The second
/// is ModDynamicFields, which controls dynamically defined fields. They are
/// set automatically by the console constructor code.
///
/// @nosubgrouping
class SimObject: public ConsoleObject
{
   typedef ConsoleObject Parent;

   friend class SimManager;
   friend class SimGroup;
   friend class SimNameDictionary;
   friend class SimManagerNameDictionary;
   friend class SimIdDictionary;

   //-------------------------------------- Structures and enumerations
  private:

   /// Flags for use in mFlags
   enum {
      Deleted   = BIT(0),   ///< This object is marked for deletion.
      Removed   = BIT(1),   ///< This object has been unregistered from the object system.
      Added     = BIT(3),   ///< This object has been registered with the object system.
      Selected  = BIT(4),   ///< This object has been marked as selected. (in editor)
      Expanded  = BIT(5),   ///< This object has been marked as expanded. (in editor)
      ModStaticFields  = BIT(6),    ///< The object allows you to read/modify static fields
      ModDynamicFields = BIT(7)     ///< The object allows you to read/modify dynamic fields
   };
  public:
   /// @name Notification
   /// @{
   struct Notify {
      enum Type {
         ClearNotify,   ///< Notified when the object is cleared.
         DeleteNotify,  ///< Notified when the object is deleted.
         ObjectRef,     ///< Cleverness to allow tracking of references.
         Invalid        ///< Mark this notification as unused (used in freeNotify).
      } type;
      void *ptr;        ///< Data (typically referencing or interested object).
      Notify *next;     ///< Next notification in the linked list.
   };

   /// @}

   enum WriteFlags {
      SelectedOnly = BIT(0) ///< Passed to SimObject::write to indicate that only objects
                            ///  marked as selected should be outputted. Used in SimSet.
   };

  private:
   // dictionary information stored on the object
   StringTableEntry objectName;
   SimObject*       nextNameObject;
   SimObject*       nextManagerNameObject;
   SimObject*       nextIdObject;

   SimGroup*   mGroup;  ///< SimGroup we're contained in, if any.
   BitSet32    mFlags;

   /// @name Notification
   /// @{
   Notify*     mNotifyList;
   /// @}

  protected:
   SimObjectId mId;         ///< Id number for this object.
   Namespace*  mNameSpace;
   U32         mTypeMask;

  protected:
   /// @name Notification
   /// Helper functions for notification code.
   /// @{

   static SimObject::Notify *mNotifyFreeList;
   static SimObject::Notify *allocNotify();     ///< Get a free Notify structure.
   static void freeNotify(SimObject::Notify*);  ///< Mark a Notify structure as free.

   /// @}

  private:
   SimFieldDictionary *mFieldDictionary;    ///< Storage for dynamic fields.

  public:
   /// @name Accessors
   /// @{

   /// Get the value of a field on the object.
   ///
   /// See @ref simobject_console "here" for a detailed discussion of what this
   /// function does.
   ///
   /// @param   slotName    Field to access.
   /// @param   array       String containing index into array
   ///                      (if field is an array); if NULL, it is ignored.
   const char *getDataField(StringTableEntry slotName, const char *array);

   /// Set the value of a field on the object.
   ///
   /// See @ref simobject_console "here" for a detailed discussion of what this
   /// function does.
   ///
   /// @param   slotName    Field to access.
   /// @param   array       String containing index into array; if NULL, it is ignored.
   /// @param   value       Value to store.
   void setDataField(StringTableEntry slotName, const char *array, const char *value);

   /// Get reference to the dictionary containing dynamic fields.
   ///
   /// See @ref simobject_console "here" for a detailed discussion of what this
   /// function does.
   ///
   /// This dictionary can be iterated over using a SimFieldDictionaryIterator.
   SimFieldDictionary * getFieldDictionary() {return(mFieldDictionary);}

   /// @}

   /// @name Initialization
   /// @{

   ///
   SimObject();
   virtual ~SimObject();

   virtual bool processArguments(S32 argc, const char **argv);  ///< Process constructor options. (ie, new SimObject(1,2,3))

   /// @}

   /// @name Events
   /// @{
   virtual bool onAdd();                                ///< Called when the object is added to the sim.
   virtual void onRemove();                             ///< Called when the object is removed from the sim.
   virtual void onGroupAdd();                           ///< Called when the object is added to a SimGroup.
   virtual void onGroupRemove();                        ///< Called when the object is removed from a SimGroup.
   virtual void onNameChange(const char *name);         ///< Called when the object's name is changed.
   virtual void onStaticModified(const char* slotName); ///< Called when a static field is modified.
                                                        ///
                                                        ///  Specifically, this is called by setDataField
                                                        ///  when a static field is modified, see
                                                        ///  @ref simobject_console "the console details".

   /// Called before any property of the object is changed in the world editor.
   ///
   /// The calling order here is:
   ///  - inspectPreApply()
   ///  - ...
   ///  - calls to setDataField()
   ///  - ...
   ///  - inspectPostApply()
   virtual void inspectPreApply();

   /// Called after any property of the object is changed in the world editor.
   ///
   /// @see inspectPreApply
   virtual void inspectPostApply();

   /// Called when a SimObject is deleted.
   ///
   /// When you are on the notification list for another object
   /// and it is deleted, this method is called.
   virtual void onDeleteNotify(SimObject *object);

   /// Called when the editor is activated.
   virtual void onEditorEnable(){};

   /// Called when the editor is deactivated.
   virtual void onEditorDisable(){};

   /// @}

   /// Find a named sub-object of this object.
   ///
   /// This is subclassed in the SimGroup and SimSet classes.
   ///
   /// For a single object, it just returns NULL, as normal objects cannot have children.
   virtual SimObject *findObject(const char *name);

   /// @name Notification
   /// @{
   Notify *removeNotify(void *ptr, Notify::Type);   ///< Remove a notification from the list.
   void deleteNotify(SimObject* obj);               ///< Notify an object when we are deleted.
   void clearNotify(SimObject* obj);                ///< Notify an object when we are cleared.
   void clearAllNotifications();                    ///< Remove all notifications for this object.
   void processDeleteNotifies();                    ///< Send out deletion notifications.

   /// Register a reference to this object.
   ///
   /// You pass a pointer to your reference to this object.
   ///
   /// When the object is deleted, it will null your
   /// pointer, ensuring you don't access old memory.
   ///
   /// @param obj   Pointer to your reference to the object.
   void registerReference(SimObject **obj);

   /// Unregister a reference to this object.
   ///
   /// Remove a reference from the list, so that it won't
   /// get nulled inappropriately.
   ///
   /// Call this when you're done with your reference to
   /// the object, especially if you're going to free the
   /// memory. Otherwise, you may erroneously get something
   /// overwritten.
   ///
   /// @see registerReference
   void unregisterReference(SimObject **obj);

   /// @}

   /// @name Registration
   ///
   /// SimObjects must be registered with the object system.
   /// @{


   /// Register an object with the object system.
   ///
   /// This must be called if you want to keep the object around.
   /// In the rare case that you will delete the object immediately, or
   /// don't want to be able to use Sim::findObject to locate it, then
   /// you don't need to register it.
   ///
   /// registerObject adds the object to the global ID and name dictionaries,
   /// after first assigning it a new ID number. It calls onAdd(). If onAdd fails,
   /// it unregisters the object and returns false.
   ///
   /// If a subclass's onAdd doesn't eventually call SimObject::onAdd(), it will
   /// cause an assertion.
   bool registerObject();

   /// Register the object, forcing the id.
   ///
   /// @see registerObject()
   /// @param   id  ID to assign to the object.
   bool registerObject(U32 id);

   /// Register the object, assigning the name.
   ///
   /// @see registerObject()
   /// @param   name  Name to assign to the object.
   bool registerObject(const char *name);

   /// Register the object, assigning a name and ID.
   ///
   /// @see registerObject()
   /// @param   name  Name to assign to the object.
   /// @param   id  ID to assign to the object.
   bool registerObject(const char *name, U32 id);

   /// Unregister the object from Sim.
   ///
   /// This performs several operations:
   ///  - Sets the removed flag.
   ///  - Call onRemove()
   ///  - Clear out notifications.
   ///  - Remove the object from...
   ///      - its group, if any. (via getGroup)
   ///      - Sim::gNameDictionary
   ///      - Sim::gIDDictionary
   ///  - Finally, cancel any pending events for this object (as it can't receive them now).
   void unregisterObject();

   void deleteObject();     ///< Unregister, mark as deleted, and free the object.
                            ///
                            /// This helper function can be used when you're done with the object
                            /// and don't want to be bothered with the details of cleaning it up.

   /// @}

   /// @name Accessors
   /// @{
   SimObjectId getId() const { return mId; }
   const char* getIdString();
   U32         getType() const  { return mTypeMask; }
   const char* getName() const { return objectName; };

   void setId(SimObjectId id);
   void assignName(const char* name);
   SimGroup* getGroup() const { return mGroup; }
   bool isProperlyAdded() const { return mFlags.test(Added); }
   bool isDeleted() const { return mFlags.test(Deleted); }
   bool isRemoved() const { return mFlags.test(Deleted | Removed); }
   bool isLocked();
   void setLocked( bool b );
   bool isHidden();
   void setHidden(bool b);

   /// @}

   /// @name Sets
   ///
   /// The object must be properly registered before you can add/remove it to/from a set.
   ///
   /// All these functions accept either a name or ID to identify the set you wish
   /// to operate on. Then they call addObject or removeObject on the set, which
   /// sets up appropriate notifications.
   ///
   /// An object may be in multiple sets at a time.
   /// @{
   bool addToSet(SimObjectId);
   bool addToSet(const char *);
   bool removeFromSet(SimObjectId);
   bool removeFromSet(const char *);

   /// @}

   /// @name Serialization
   /// @{

   /// Output the TorqueScript to recreate this object.
   ///
   /// This calls writeFields internally.
   /// @param   stream  Stream to output to.
   /// @param   tabStop Indentation level for this object.
   /// @param   flags   If SelectedOnly is passed here, then
   ///                  only objects marked as selected (using setSelected)
   ///                  will output themselves.
   virtual void write(Stream &stream, U32 tabStop, U32 flags = 0);

   /// Write the fields of this object in TorqueScript.
   ///
   /// @param   stream  Stream for output.
   /// @param   tabStop Indentation level for the fields.
   void writeFields(Stream &stream, U32 tabStop);

   /// Copy fields from another object onto this one.
   ///
   /// Objects must be of same type. Everything from obj
   /// will overwrite what's in this object; extra fields
   /// in this object will remain.
   ///
   /// @param   obj Object to copy from.
   void assignFieldsFrom(SimObject *obj);

   /// @}

   /// Return the object's namespace.
   Namespace* getNamespace() { return mNameSpace; }

   /// Get next matching item in namespace.
   ///
   /// This wraps a call to Namespace::tabComplete; it gets the
   /// next thing in the namespace, given a starting value
   /// and a base length of the string. See
   /// Namespace::tabComplete for details.
   const char *tabComplete(const char *prevText, S32 baseLen, bool);

   /// @name Accessors
   /// @{
   bool isSelected() const { return mFlags.test(Selected); }
   bool isExpanded() const { return mFlags.test(Expanded); }
   void setSelected(bool sel) { if(sel) mFlags.set(Selected); else mFlags.clear(Selected); }
   void setExpanded(bool exp) { if(exp) mFlags.set(Expanded); else mFlags.clear(Expanded); }
   void setModDynamicFields(bool dyn) { if(dyn) mFlags.set(ModDynamicFields); else mFlags.clear(ModDynamicFields); }
   void setModStaticFields(bool sta) { if(sta) mFlags.set(ModStaticFields); else mFlags.clear(ModStaticFields); }

   /// @}

   /// @name Initialization
   /// @{

   /// Called to register the object's lights, if any, with the LightManager.
   ///
   /// @param   lm               LightManager to put lights into.
   /// @param   lightingScene   True if we're currently calculating lighting information.
   virtual void registerLights(LightManager * lm, bool lightingScene) {};

   /// @}

   static void initPersistFields();
   DECLARE_CONOBJECT(SimObject);
};

//---------------------------------------------------------------------------
/// Smart SimObject pointer.
///
/// This class keeps track of the book-keeping necessary
/// to keep a registered reference to a SimObject or subclass
/// thereof.
///
/// Normally, if you want the SimObject to be aware that you
/// have a reference to it, you must call SimObject::registerReference()
/// when you create the reference, and SimObject::unregisterReference() when
/// you're done. If you change the reference, you must also register/unregister
/// it. This is a big headache, so this class exists to automatically
/// keep track of things for you.
///
/// @code
///     // Assign an object to the
///     SimObjectPtr<GameBase> mOrbitObject = Sim::findObject("anObject");
///
///     // Use it as a GameBase*.
///     mOrbitObject->getWorldBox().getCenter(&mPosition);
///
///     // And reassign it - it will automatically update the references.
///     mOrbitObject = Sim::findObject("anotherObject");
/// @endcode
template <class T> class SimObjectPtr
{
  private:
   SimObject *mObj;

  public:
   SimObjectPtr() { mObj = 0; }
   SimObjectPtr(T* ptr)
   {
      mObj = ptr;
      if(mObj)
         mObj->registerReference(&mObj);
   }
   SimObjectPtr(const SimObjectPtr<T>& rhs)
   {
      mObj = const_cast<T*>(static_cast<const T*>(rhs));
      if(mObj)
         mObj->registerReference(&mObj);
   }
   SimObjectPtr<T>& operator=(const SimObjectPtr<T>& rhs)
   {
      if(this == &rhs)
         return(*this);
      if(mObj)
         mObj->unregisterReference(&mObj);
      mObj = const_cast<T*>(static_cast<const T*>(rhs));
      if(mObj)
         mObj->registerReference(&mObj);
      return(*this);
   }
   ~SimObjectPtr()
   {
      if(mObj)
         mObj->unregisterReference(&mObj);
   }
   SimObjectPtr<T>& operator= (T *ptr)
   {
      if(mObj != (SimObject *) ptr)
      {
         if(mObj)
            mObj->unregisterReference(&mObj);
         mObj = (SimObject *) ptr;
         if (mObj)
            mObj->registerReference(&mObj);
      }
      return *this;
   }
#if defined(__MWERKS__) && (__MWERKS__ < 0x2400)
   // CW 5.3 seems to get confused comparing SimObjectPtrs...
   bool operator == (const SimObject *ptr) { return mObj == ptr; }
   bool operator != (const SimObject *ptr) { return mObj != ptr; }
#endif
   bool isNull() const   { return mObj == 0; }
   T* operator->() const { return static_cast<T*>(mObj); }
   T& operator*() const  { return *static_cast<T*>(mObj); }
   operator T*() const   { return static_cast<T*>(mObj)? static_cast<T*>(mObj) : 0; }
};

//---------------------------------------------------------------------------
/// Root DataBlock class.
///
/// @section SimDataBlock_intro Introduction
///
/// Another powerful aspect of Torque's networking is the datablock. Datablocks
/// are used to provide relatively static information about entities; for instance,
/// what model a weapon should use to display itself, or how heavy a player class is.
///
/// This gives significant gains in network efficiency, because it means that all
/// the datablocks on a server can be transferred over the network at client
/// connect time, instead of being intertwined with the update code for NetObjects.
///
/// This makes the network code much simpler overall, as one-time initialization
/// code is segregated from the standard object update code, as well as providing
/// several powerful features, which we will discuss momentarily.
///
/// @section SimDataBlock_preload preload() and File Downloading
///
/// Because datablocks are sent over the wire, using SimDataBlockEvent, before
/// gameplay starts in earnest, we gain in several areas. First, we don't have to
/// try to keep up with the game state while working with incomplete information.
/// Second, we can provide the user with a nice loading screen, instead of the more
/// traditional "Connecting..." message. Finally, and most usefully, we can request
/// missing files from the server as we become aware of them, since we are under
/// no obligation to render anything for the user.
///
/// The mechanism for this is fairly basic. After a datablock is unpacked, the
/// preload() method is called. If it returns false and sets an error, then the
/// network code checks to see if a file (or files) failed to be located by the
/// ResManager; if so, then it requests those files from the server. If preload
/// returns true, then the datablock is considered loaded. If preload returns
/// false and sets no error, then the connection is aborted.
///
/// Once the file(s) is downloaded, the datablock's preload() method is called again.
/// If it fails with the same error, the connection is aborted. If a new error is
/// returned, then the download-retry process is repeated until the preload works.
///
/// @section SimDataBlock_guide Guide To Datablock Code
///
/// To make a datablock subclass, you need to extend three functions:
///      - preload()
///      - packData()
///      - unpackData()
///
/// packData() and unpackData() simply read or write data to a network stream. If you
/// add any fields, you need to add appropriate calls to read or write. Make sure that
/// the order of reads and writes is the same in both functions. Make sure to call
/// the Parent's version of these methods in every subclass.
///
/// preload() is a bit more complex; it is responsible for taking the raw data read by
/// unpackData() and processing it into a form useful by the datablock's owning object. For
/// instance, the Player class' datablock, PlayerData, gets handles to commonly used
/// nodes in the player model, as well as resolving handles to textures and other
/// resources. <b>Any</b> code which loads files or performs other actions beyond simply
/// reading the data from the packet, such as validation, must reside in preload().
///
/// To write your own preload() methods, see any of the existing methods in the codebase; for instance,
/// PlayerData::preload() is an excellent example of error-reporting, data validation, and so forth.
///
/// @note A useful trick, which is used in several places in the engine, is that of temporarily
///       storing SimObjectIds in the variable which will eventually hold the "real" handle. ShapeImage
///       uses this trick in several pllaces; so do the vehicle classes. See GameBaseData for more on
///       using this trick.
///
/// @see GameBaseData for some more information on the datablocks used throughout
///      most of the engine.
/// @see http://hosted.tribalwar.com/t2faq/datablocks.shtml for an excellent
///      explanation of the basics of datablocks from script. Note that these comments
///      mostly apply to GameBaseData and its children.
/// @nosubgrouping
class SimDataBlock: public SimObject
{
   typedef SimObject Parent;
public:

   SimDataBlock();
   DECLARE_CONOBJECT(SimDataBlock);

   /// @name Datablock Internals
   /// @{

protected:
   S32  modifiedKey;

  public:
   static SimObjectId sNextObjectId;
   static S32         sNextModifiedKey;

   /// Assign a new modified key.
   ///
   /// Datablocks are assigned a modified key which is updated every time
   /// a static field of the datablock is changed. These are gotten from
   /// a global store.
   static S32 getNextModifiedKey() { return sNextModifiedKey; }

   /// Get the modified key for this particular datablock.
   S32 getModifiedKey() const { return modifiedKey; }

   bool onAdd();
   void onStaticModified(const char* slotName);
   //void setLastError(const char*);
   void assignId();

   /// @}

   /// @name Datablock Interface
   /// @{

   ///
   virtual void packData(BitStream* stream);
   virtual void unpackData(BitStream* stream);

   /// Called to prepare the datablock for use, after it has been unpacked.
   ///
   /// @param  server      Set if we're running on the server (and therefore don't need to load
   ///                     things like textures or sounds).
   /// @param  errorBuffer If an error occurs in loading, this is set to a short string describing
   ///                     the error.
   /// @returns True if all went well; false if something failed.
   ///
   /// @see @ref SimDataBlock_preload
   virtual bool preload(bool server, char errorBuffer[256]);
   /// @}
};

//---------------------------------------------------------------------------
/// A set of SimObjects.
///
/// It is often necessary to keep track of an arbitrary set of SimObjects.
/// For instance, Torque's networking code needs to not only keep track of
/// the set of objects which need to be ghosted, but also the set of objects
/// which must <i>always</i> be ghosted. It does this by working with two
/// sets. The first of these is the RootGroup (which is actually a SimGroup)
/// and the second is the GhostAlwaysSet, which contains objects which must
/// always be ghosted to the client.
///
/// Some general notes on SimSets:
///     - Membership is not exclusive. A SimObject may be a member of multiple
///       SimSets.
///     - A SimSet does not destroy subobjects when it is destroyed.
///     - A SimSet may hold an arbitrary number of objects.
///
/// Using SimSets, the code to work with these two sets becomes
/// relatively straightforward:
///
/// @code
///        // (Example from netObject.cc)
///        // To iterate over all the objects in the Sim:
///        for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj)
///        {
///                  NetObject* nobj = dynamic_cast<NetObject*>(*obj);
///
///                 if (nobj)
///                   {
///                     // ... do things ...
///                 }
///         }
///
///         // (Example from netGhost.cc)
///         // To iterate over the ghostAlways set.
///         SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet();
///         SimSet::iterator i;
///
///         U32 sz = ghostAlwaysSet->size();
///         S32 j;
///
///         for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++)
///         {
///             NetObject *obj = (NetObject *)(*i);
///
///             /// ... do things with obj...
///         }
/// @endcode
///

class SimSet: public SimObject
{
   typedef SimObject Parent;
  protected:
   SimObjectList objectList;
   void *mMutex;

  public:
   SimSet() {
      VECTOR_SET_ASSOCIATION(objectList);

      mMutex = Mutex::createMutex();
   }

   ~SimSet()
   {
      lock();
      unlock();
      Mutex::destroyMutex(mMutex);
      mMutex = NULL;
   }

   /// @name STL Interface
   /// @{

   ///
   typedef SimObjectList::iterator iterator;
   typedef SimObjectList::value_type value;
   SimObject* front() { return objectList.front(); }
   SimObject* first() { return objectList.first(); }
   SimObject* last()  { return objectList.last(); }
   bool       empty() { return objectList.empty();   }
   S32        size()  { return objectList.size(); }
   iterator   begin() { return objectList.begin(); }
   iterator   end()   { return objectList.end(); }
   value operator[] (S32 index) { return objectList[U32(index)]; }

   iterator find( iterator first, iterator last, SimObject *obj)
   { return ::find(first, last, obj); }

   bool reOrder( SimObject *obj, SimObject *target=0 );

   /// @}

   virtual void onRemove();
   virtual void onDeleteNotify(SimObject *object);

   /// @name Set Management
   /// @{

   virtual void addObject(SimObject*);      ///< Add an object to the set.
   virtual void removeObject(SimObject*);   ///< Remove an object from the set.

   virtual void pushObject(SimObject*);     ///< Add object to end of list.
                                            ///
                                            /// It will force the object to the end of the list if it already exists
                                            /// in the list.

   virtual void popObject();                ///< Remove an object from the end of the list.

   void bringObjectToFront(SimObject* obj) { reOrder(obj, front()); }
   void pushObjectToBack(SimObject* obj) { reOrder(obj, NULL); }

   /// @}

   void write(Stream &stream, U32 tabStop, U32 flags = 0);

   virtual SimObject *findObject(const char *name);

   inline void lock()
   {
#ifdef TORQUE_MULTITHREAD
      Mutex::lockMutex(mMutex);
#endif
   }

   inline void unlock()
   {
#ifdef TORQUE_MULTITHREAD
      Mutex::unlockMutex(mMutex);
#endif
   }

   DECLARE_CONOBJECT(SimSet);
};

/// Iterator for use with SimSets
///
/// @see SimSet
class SimSetIterator
{
  protected:
   struct Entry {
      SimSet* set;
      SimSet::iterator itr;
   };
   class Stack: public Vector<Entry> {
     public:
      void push_back(SimSet*);
   };
   Stack stack;

  public:
   SimSetIterator(SimSet*);
   SimObject* operator++();
   SimObject* operator*() {
      return stack.empty()? 0: *stack.last().itr;
   }
};

//---------------------------------------------------------------------------
/// A group of SimObjects.
///
/// A SimGroup is a stricter form of SimSet. SimObjects may only be a member
/// of a single SimGroup at a time.
///
/// The SimGroup will automatically enforce the single-group-membership rule.
///
/// @code
///      // From engine/sim/simPath.cc - getting a pointer to a SimGroup
///      SimGroup* pMissionGroup = dynamic_cast<SimGroup*>(Sim::findObject("MissionGroup"));
///
///      // From game/trigger.cc:46 - iterating over a SimObject's group.
///      SimObject* trigger = ...;
///      SimGroup* pGroup = trigger->getGroup();
///      for (SimGroup::iterator itr = pGroup->begin(); itr != pGroup->end(); itr++)
///      {
///         // do something with *itr
///      }
/// @endcode
class SimGroup: public SimSet
{
  private:
   friend class SimManager;
   friend class SimObject;

   typedef SimSet Parent;
   SimNameDictionary nameDictionary;

  public:
   ~SimGroup();

   /// Add an object to the group.
   void addObject(SimObject*);
   void addObject(SimObject*, SimObjectId);
   void addObject(SimObject*, const char *name);

   /// Remove an object from the group.
   void removeObject(SimObject*);

   void onRemove();

   /// Find an object in the group.
   virtual SimObject* findObject(const char* name);

   bool processArguments(S32 argc, const char **argv);

   DECLARE_CONOBJECT(SimGroup);
};

inline void SimGroup::addObject(SimObject* obj, SimObjectId id)
{
   obj->mId = id;
   addObject( obj );
}

inline void SimGroup::addObject(SimObject *obj, const char *name)
{
   addObject( obj );
   obj->assignName(name);
}

class SimGroupIterator: public SimSetIterator
{
  public:
   SimGroupIterator(SimGroup* grp): SimSetIterator(grp) {}
   SimObject* operator++();
};

//---------------------------------------------------------------------------

class SimDataBlockGroup : public SimGroup
{
  private:
   S32 mLastModifiedKey;

  public:
   static S32 QSORT_CALLBACK compareModifiedKey(const void* a,const void* b);
   void sort();
   SimDataBlockGroup();
};

//---------------------------------------------------------------------------

/// @defgroup simbase_helpermacros Helper Macros
///
/// These are used for named sets and groups in the manager.
/// @{
#define DeclareNamedSet(set) extern SimSet *g##set;inline SimSet *get##set() { return g##set; }
#define DeclareNamedGroup(set) extern SimGroup *g##set;inline SimGroup *get##set() { return g##set; }
#define ImplementNamedSet(set) SimSet *g##set;
#define ImplementNamedGroup(set) SimGroup *g##set;
/// @}
//---------------------------------------------------------------------------

namespace Sim
{
   DeclareNamedSet(ActiveActionMapSet)
   DeclareNamedSet(GhostAlwaysSet)
   DeclareNamedSet(LightSet)
   DeclareNamedSet(WayPointSet)
   DeclareNamedSet(fxReplicatorSet)
   DeclareNamedSet(fxFoliageSet)
   DeclareNamedGroup(ActionMapGroup)
   DeclareNamedGroup(ClientGroup)
   DeclareNamedGroup(GuiGroup)
   DeclareNamedGroup(GuiDataGroup)
   DeclareNamedGroup(TCPGroup)
   DeclareNamedGroup(ClientConnectionGroup)
   DeclareNamedGroup(ChunkFileGroup);

   void init();
   void shutdown();

   SimDataBlockGroup *getDataBlockGroup();
   SimGroup* getRootGroup();

   SimObject* findObject(SimObjectId);
   SimObject* findObject(const char* name);
   template<class T> inline bool findObject(SimObjectId id,T*&t)
   {
      t = dynamic_cast<T*>(findObject(id));
      return t != NULL;
   }
   template<class T> inline bool findObject(const char *objectName,T*&t)
   {
      t = dynamic_cast<T*>(findObject(objectName));
      return t != NULL;
   }

   void advanceToTime(SimTime time);
   void advanceTime(SimTime delta);
   SimTime getCurrentTime();
   SimTime getTargetTime();

   /// a target time of 0 on an event means current event
   U32 postEvent(SimObject*, SimEvent*, U32 targetTime);

   inline U32 postEvent(SimObjectId id,SimEvent*evt, U32 targetTime)
   {
      return postEvent(findObject(id), evt, targetTime);
   }
   inline U32 postEvent(const char *objectName,SimEvent*evt, U32 targetTime)
   {
      return postEvent(findObject(objectName), evt, targetTime);
   }
   inline U32 postCurrentEvent(SimObject*obj, SimEvent*evt)
   {
      return postEvent(obj,evt,getCurrentTime());
   }
   inline U32 postCurrentEvent(SimObjectId obj,SimEvent*evt)
   {
      return postEvent(obj,evt,getCurrentTime());
   }
   inline U32 postCurrentEvent(const char *obj,SimEvent*evt)
   {
      return postEvent(obj,evt,getCurrentTime());
   }

   void cancelEvent(U32 eventId);
   bool isEventPending(U32 eventId);
   U32  getEventTimeLeft(U32 eventId);
   U32  getTimeSinceStart(U32 eventId);
   U32  getScheduleDuration(U32 eventId);
}

//----------------------------------------------------------------------------
#define DECLARE_CONSOLETYPE(T) \
   DefineConsoleType( Type##T##Ptr )

#define IMPLEMENT_CONSOLETYPE(T) \
   DatablockConsoleType( T##Ptr, Type##T##Ptr, sizeof(T*), T )

#define IMPLEMENT_SETDATATYPE(T) \
   ConsoleSetType( Type##T##Ptr ) \
   {                                                                                                 \
      volatile SimDataBlock* pConstraint = static_cast<SimDataBlock*>((T*)NULL);                     \
                                                                                                     \
      if (argc == 1) {                                                                               \
         *reinterpret_cast<T**>(dptr) = NULL;                                                        \
         if (argv[0] && argv[0][0] && !Sim::findObject(argv[0],*reinterpret_cast<T**>(dptr)))        \
            Con::printf("Object '%s' is not a member of the '%s' data block class", argv[0], #T);    \
      }                                                                                              \
      else                                                                                           \
         Con::printf("Cannot set multiple args to a single pointer.");                               \
   }

#define IMPLEMENT_GETDATATYPE(T) \
   ConsoleGetType( Type##T##Ptr ) \
   {                                                                                   \
      volatile SimDataBlock* pConstraint = static_cast<SimDataBlock*>((T*)NULL);       \
      T** obj = reinterpret_cast<T**>(dptr);                                           \
      return *obj ? (*obj)->getName() : "";                                            \
   }

//---------------------------------------------------------------------------

#endif
