#ifndef BABBLEMUD_OBJECT_H
#define BABBLEMUD_OBJECT_H

#include <stdexcept>
#include <list>

#include "thing.h"
#include "compose.h"
#include "strings.h"

/* 

  An object in the MUD has various attributes (stored by name in the "thing" class)
  plus might contain other objects. eg. a bag might contain many items.
  
  We recursively define Object so that an object can contain multiple levels of
  other objects.

*/

class Object;
typedef list<Object> ObjectList; 
typedef map<string, ObjectList> ObjectListMap; 

class Object : public thing
{
  
  public:

  friend ostream& operator<< (ostream & os, const Object & obj);
  
  // helper function for calculating an object's total (something)
  template <typename T>
  struct fGetTotal 
    {
    const string & what_;    // eg. "weight"
    T total;                // running total
    fGetTotal (const string & what) : what_ (what), total (0) {}
    bool operator() (const Object & obj) 
      { total += obj.getTotal<T> (what_); }
    T GetTotal () const { return total; }
    };  // end of fGetTotal

  // ctor
  Object (const string & name = "") // allow you to name it on creation
    {
    if (!name.empty ())
      sets ("name", name);
    }
  
 // ctor to construct an object from a straight thing (ie. no contents)
  Object (const thing & rhs)
    {
    *this = rhs;
    }

 // see if two objects are equal
  bool operator== (const Object & rhs) const
    {
    return thing::operator==(rhs) &&  // base object (ie. the actual object)
           contents_ == rhs.contents_;  // and its contents_
    }
  
  void NeedFlag   (const string & name,   // flag must be set
                   const string & message = "",
                   const bool lookedUp = false) const;    // true if message, false if code

  void NeedNoFlag (const string & name,   // flag must not be set
                   const string & message = "",
                   const bool lookedUp = false) const;    // true if message, false if code
    
  // get the total of all items (eg. total weight)
  // its weight is the weight of itself plus the combined weight of its contents_
  template <typename T>
  T getTotal (const string & what) const 
    {
    return for_each (ISEQ (contents_), fGetTotal<T> (what)).GetTotal ()
                    + get<T> (*this, what);
    }
  
  const int     getiTotal (const string & what)  const { return getTotal<int> (what); }
  const double  getfTotal (const string & what)  const { return getTotal<double> (what); }
  
  // objects can contain lists of other objects (eg. inventory items containing other items)
  ObjectList contents_;  
  
};

typedef ObjectList::iterator ObjectIter;

class TopThing : public thing
  {
  public:
  
  friend ostream& operator<< (ostream & os, const TopThing & obj);
                  
 // TopThings can contain multiple lists of other objects (a map of lists)
  ObjectListMap items_;  
  
  };  // end of class TopThing

ObjectList::const_iterator findItem (const string & what, 
                                     const ObjectList & contents,
                                     const string & message);

ObjectList::iterator findItem (const string & what, 
                               ObjectList & contents,
                               const string & message);

void moveItem (ObjectList & source,        // source container
              ObjectList::iterator item,   // iterator to item
              ObjectList & destination);   // destination container

ostream& writeList (ostream & os, const string & name, const ObjectList & contents_);

// find an iterator to a object in a list of objects, matching key=target
// where key evaluates to a regular expression
// eg, container might be a list of exits
//     key might be "match"  (what exit to match)
//     target might be "north"
//     and in a particular exit match="n|north"
ObjectList::const_iterator FindObjectRegexp (const ObjectList & container,
                                       const string & key,
                                       const string & target,
                                       StringMap & wildcards);

// non-const version
ObjectList::iterator FindObjectRegexp (ObjectList & container,
                                       const string & key,
                                       const string & target,
                                       StringMap & wildcards);

// find an iterator to a object in a list of objects, matching key=target
// where key matches exactly
// eg, a command handler
ObjectList::iterator FindObject (ObjectList & container, 
                                 const string & key, 
                                 const string & target);

// -- integer version
ObjectList::iterator FindObject (ObjectList & container, 
                                 const string & key, 
                                 const int target);

const string shortDesc (const Object & object, const string & prefix);
const string longDesc (const Object & object, const string & prefix);

void NotPermitted (const string & message, const bool lookedUp = false); // can't do that!

void GiveUniqueID (Object & o);

#endif // BABBLEMUD_OBJECT_H
