#ifndef BABBLEMUD_THING_H
#define BABBLEMUD_THING_H

#include <string>
#include <map>

using namespace std;

/*******************************************************************
    A basic_thing
 *******************************************************************/

// a basic_thing is a fundamental collection of data (eg. one player, one room)
// it has attributes (eg. player name, strength)
// attributes can be:
//   s: string
//   i: integer (ie. an integral number)
//   f: float   (internally F, a floating-point number)
//   b: bool    (true or false)

/*


Supported operations:

  copy constructor, assignment, clear all data
  compare equal (ie. are two things absolutely identical?)
  get number of attributes (sizes, sizei, sizef, sizeb, size)
  is basic_thing totally empty? (empty)

  get data with optional default (gets, geti, getf, getb)
  set data to new value (sets, seti, setf, setb)
  is data present?  (has, hass, hasi, hasf, hasb)
  erase attributes  (erases, erasei, erasef, eraseb)
 
  you can also get the maps as const so they can be iterated through

  The difference between getting (geti) and checking if present (hasi) is
  that getting will always return a value (eg. 0) whereas hasi will tell you
  if the attribute even exists or not.

  Erase erases by name - ie. removes it entirely.

*/

template <typename S = string,
          typename I = int,
          typename F = double,
          typename B = bool>
class basic_thing
  {

  public:

  friend ostream& operator<< (ostream & o, const basic_thing & p);


  // As far as I can tell there's no need for these to be friends
  // since 'get' calls public methods ... and there's a need for
  // them to not be friends, namely that VS won't compile :-)
  //     -dhaley aug-15-04

  /*friend const S myget<S> (const basic_thing & t, const string & n);  
  friend const I myget<I> (const basic_thing & t, const string & n);  
  friend const F myget<F> (const basic_thing & t, const string & n);  
  friend const B myget<B> (const basic_thing & t, const string & n);*/
    
  // return data by key - accepts default argument in case not found
  const S gets (const string & skey, 
                const S & sdefault = S ()) const
    { 
    typename map <string, S>::const_iterator i = sdata_.find (skey);
    if (i == sdata_.end ())
        return sdefault;
    else
      return i->second;
    }

  const I geti (const string & skey, 
          const I & idefault = I ()) const
    { 
    typename map <string, I>::const_iterator i = idata_.find (skey);
    if (i == idata_.end ())
      return idefault;
    else
      return i->second;
    }

  const F getf (const string & skey, 
          const F & fdefault = F ()) const
    { 
    typename map <string, F>::const_iterator i = fdata_.find (skey);
    if (i == fdata_.end ())
      return fdefault;
    else
      return i->second;
    }

  const B getb (const string & skey, 
          const B & bdefault = B ()) const
    { 
    typename map <string, B>::const_iterator i = bdata_.find (skey);
    if (i == bdata_.end ())
      return bdefault;
    else
      return i->second;
    }

  // see if data present 
  const bool hass (const string & skey) const
    { 
    return sdata_.find (skey) != sdata_.end ();
    }

  const bool hasi (const string & skey) const
    { 
    return idata_.find (skey) != idata_.end ();
    }

  const bool hasf (const string & skey) const
    { 
    return fdata_.find (skey) != fdata_.end ();
    }

  const bool hasb (const string & skey) const
    { 
    return bdata_.find (skey) != bdata_.end ();
    }

  // returns true if that key is used in *any* of the maps
  const bool has (const string & skey)  const
    {
    return hass (skey) ||
           hasi (skey) ||
           hasf (skey) ||
           hasb (skey);
    }

  // get counts of items in maps
  const int sizes () const { return sdata_.size (); }
  const int sizei () const { return idata_.size (); }
  const int sizef () const { return fdata_.size (); }
  const int sizeb () const { return bdata_.size (); }

  // get count of all attributes
  const int size  () const 
      { 
      return sizes () +
             sizei () +
             sizef () +
             sizeb ();
      }

  // are all attributes empty?
  const bool empty () const
    {
      return sdata_.empty () &&
             idata_.empty () &&
             fdata_.empty () &&
             bdata_.empty ();
    }

  // set data by key 
  void sets (const string & skey, const S & sval) 
    { 
    sdata_ [skey] = sval;
    }

  void seti (const string & skey, const I & ival) 
    { 
    idata_ [skey] = ival;
    }

  void setf (const string & skey, const F & fval) 
    { 
    fdata_ [skey] = fval;
    }

  void setb (const string & skey, const B & bval) 
    { 
    bdata_ [skey] = bval;
    }

  // erase attributes by name
  void erases (const string & skey) { sdata_.erase (skey); }
  void erasei (const string & skey) { idata_.erase (skey); }
  void erasef (const string & skey) { fdata_.erase (skey); }
  void eraseb (const string & skey) { bdata_.erase (skey); }
  
  // erase all attributes which match the key (any type_)
  void erase  (const string & skey)
    {
    sdata_.erase (skey);     
    idata_.erase (skey);
    fdata_.erase (skey);      
    bdata_.erase (skey);      
    }
  
  // clear all data
  void clear ()
    {
    sdata_.clear ();
    idata_.clear ();
    fdata_.clear ();
    bdata_.clear ();
    } // end of clear

  // return the maps as const so we can iterate through them (eg. to list them)
  const map <string, S>  & get_sdata () const { return sdata_; }
  const map <string, I>  & get_idata () const { return idata_; }
  const map <string, F>  & get_fdata () const { return fdata_; }
  const map <string, B>  & get_bdata () const { return bdata_; }

  // see if two things are equal
  const bool operator== (const basic_thing & rhs) const
    {
    return sdata_ == rhs.sdata_ &&
           idata_ == rhs.idata_ &&
           fdata_ == rhs.fdata_ &&
           bdata_ == rhs.bdata_;
    }
     
  private:

  // internal data of the basic_thing
  map <string, S> sdata_;     // strings
  map <string, I> idata_;     // integers
  map <string, F> fdata_;     // floats
  map <string, B> bdata_;     // bools

  }; // end of class basic_thing

typedef basic_thing<string, int, double, bool> thing;

// rather silly helper functions to enable me to
// template the getTotal routine. 
// don't know if it is worth it. :(

template <typename T>
inline const T get (const thing & t, const string & skey); 
  
template <>
inline const string get<string> (const thing & t, const string & skey)
  {
  return t.gets (skey);  
  }
  
template <>
inline const int get<int> (const thing & t, const string & skey)
  {
  return t.geti (skey);  
  }
   
template <>
inline const double get<double> (const thing & t, const string & skey)
  {
  return t.getf (skey);  
  }  
  
template <>
inline const bool get<bool> (const thing & t, const string & skey)
  {
  return t.getb (skey);  
  } 

// copy some sort of thing to another (eg. object to thing, thing to room etc.)
template <typename T1, typename T2>
void CopyThing (const T1 & sce, T2 & dest, const bool clear = false)
  {
  // erase destination if wanted
  if (clear)
    dest.clear ();
  
  // strings
  for (map<string, string>::const_iterator ss = sce.get_sdata ().begin ();
       ss != sce.get_sdata ().end ();
       ++ss)
    dest.sets (ss->first, ss->second);

  // numbers
  for (map<string, int>::const_iterator si = sce.get_idata ().begin ();
       si != sce.get_idata ().end ();
       ++si)
    dest.seti (si->first, si->second);

  // floats
  for (map<string, double>::const_iterator sf = sce.get_fdata ().begin ();
       sf != sce.get_fdata ().end ();
       ++sf)
    dest.setf (sf->first, sf->second);

  // bools
  for (map<string, bool>::const_iterator sb = sce.get_bdata ().begin ();
       sb != sce.get_bdata ().end ();
       ++sb)
    dest.setb (sb->first, sb->second);

  }

string HashThing (const thing & t);

typedef map<string, thing> ThingMap;


#endif // BABBLEMUD_THING_H

