/*

 BabbleMud - A MUD server

 Author:  Nick Gammon 
          http://www.gammon.com.au/ 

(C) Copyright Nick Gammon 2004. Permission to copy, use, modify, sell and
distribute this software is granted provided this copyright notice appears
in all copies. This software is provided "as is" without express or implied
warranty, and with no claim as to its suitability for any purpose.
 
*/

#include "commands.h"
#include "save.h"
      
// helper function for get a flag
string GetFlag (const string & flag)
  {
  if (flag.empty ())
    throw msg ("no_flag");
  if (flag.find_first_not_of (valid_flag_name) != string::npos)
    throw msg ("flag_not_valid");
  return flag;      
  } // end of GetFlag 

void DoSetFlag (CommandInfo & info)
{
Character & target (info.target ());

  string flag = GetFlag (info.wildcards_ ["flag"]); // what

  if (target.HaveFlag (flag))    // check not set
    throw msg ("flag_already_set");
  
  target.setb (flag, true);   // set it
      
} // end of DoSetFlag

void DoClearFlag (CommandInfo & info)
{
 Character & target (info.target ());

  string flag = GetFlag (info.wildcards_ ["flag"]); // what

  if (!target.HaveFlag (flag))    // check set
    throw msg ("flag_not_set");
  
  target.setb (flag, false);   // clear it
      
} // end of DoClearFlag

void DoClone (CommandInfo & info)
{
Character & p (info.player ());
     
  ObjectList::const_iterator i = 
    getAnObject (info.wildcards_ ["what"], p.inventory (), 
        "clone_what", "not_in_inventory"  );

  info.sub_ ["what"] = *i;
  info.sub_ ["you"].sets ("short", p.message ("you"));
  
  CanTakeItem (p, info, *i, false);

  Object obj = *i;    // make a copy
  GiveUniqueID (obj); // give object and all its children unique IDs
  p.inventory ().push_back (obj);   // put copy in inventory
    
} // end of DoClone

void DoInfo (CommandInfo & info)
{
  ostringstream os;
  os << info.target ();
  info.player ().send (os.str ());
} // end of DoInfo

void DoCinfo (CommandInfo & info)
{
  ostringstream os;
  os << control ();
  info.player ().send (os.str ());
} // end of DoCinfo

// "write control file"
//  - saves all control information
void DoWriteControl (CommandInfo & info)
{
Character & p (info.player ());
  WriteTopThing (CONTROL_FILE, "control", control ());
  p.send ("done", messagemap ());
} // end of DoWriteControl

// show things
template <typename T>
struct showAttribute
{
  Character & player_;
  // ctor
  showAttribute (Character & player, const string & name) : player_ (player) 
    {
    player_ .send ("  " + name);
    }
    
  void operator() (const pair<string, T> & data)
    {
    player_.send (MAKE_STRING ("    " << data.first << " = " << data.second));  
    }
};  // end of showAttribute

// general thing-shower - used for displays (not XML-writing)
void ShowThing (Character & player, const thing & o)
  {
  
  // string data
  if (!o.get_sdata ().empty ())
    for_each_noreturn (ISEQ (o.get_sdata ()), showAttribute<string> (player, "(Strings)"));  
  
  // int data
  if (!o.get_idata ().empty ())
    for_each_noreturn (ISEQ (o.get_idata ()), showAttribute<int> (player, "(Numbers)"));  
  
  // float data
  if (!o.get_fdata ().empty ())
    for_each_noreturn (ISEQ (o.get_fdata ()), showAttribute<double> (player, "(Floats)"));  
  
  // bool data
  if (!o.get_bdata ().empty ())
    for_each_noreturn (ISEQ (o.get_bdata ()), showAttribute<bool> (player, "(Bools)"));  

  } // end of ShowThing

struct fShowInfoEffect
{
  Character & player_;
  int count_;

   // ctor
  fShowInfoEffect (Character & player) 
    : player_ (player), count_ (0) {}
  void operator() (const Object & o) 
    {
    player_.send (MAKE_STRING (player_.message ("effect") << ++count_ << ":"));
    ShowThing (player_, o);
   } // end of operator()
  
};  // end of fShowInfoEffect

void DoSpellInfo (CommandInfo & info)
{
Character & p (info.player ());
Object & spell = info.spell ();

  ShowThing (p, spell);
  p.send ("effectslist", messagemap ());
  for_each (ISEQ (spell.contents_), fShowInfoEffect (p));
  p.send ("");  // newline
 
} // end of DoSpellInfo

struct fShowOneSpell
{
  Character & player_;
  // ctor
  fShowOneSpell (Character & player) : player_ (player){}
  void operator() (const Object & o) 
    {
    string inactive;
    if (!o.getb ("active"))
      inactive = " (" + player_.message ("inactive") + ")";
    
    player_.send (MAKE_STRING (setw (15) << firstLetterCaps (o.gets ("name")) 
            << " - " << o.contents_.size () << " effect(s)"
            << inactive));
    } // end of operator()
  
};  // end of fShowOneSpell

void DoShowSpells (CommandInfo & info)
{
Character & p (info.player ());
ObjectList & spells = control ().items_ ["spells"];

  p.send ("allspells", messagemap ());
  for_each (ISEQ (spells), fShowOneSpell (p));

} // end of DoShowSpells

struct fShowOneEvent
{
  Character & player_;
  int count_;

  // ctor
  fShowOneEvent (Character & player) : player_ (player), count_ (0) {}
  void operator() (const Event & e) 
    {
    player_.send (MAKE_STRING("Event #" << ++count_ << " - type=" << e.gets ("type")
            << ", interval=" << e.GetInterval ()
            << ", instance=" << e.GetInstance () 
            << ", count=" << e.GetCount ()));
    ShowThing (player_, e);
    
    } // end of operator()
  
};  // end of fShowOneEvent

void DoShowEvents (CommandInfo & info)
{
Character & p (info.player ());
EventQueue & mainQueue = eventQueue ();
 
  if (mainQueue.empty ())
    throw msg ("no_events");
  
  p.send (MAKE_STRING ("Event queue size = " << mainQueue.size ()));

  // this is kind of fiddly because we can't just iterate through the
  // queue - we have to make a copy, and work on that
  
  list<Event> q;   // make copy of queue
  while (!mainQueue.empty ())
    {
    Event e = mainQueue.top ();
    mainQueue.pop ();
    q.push_back (e);    // add to our list
    }
  
  // put copy back into queue
  copy (q.rbegin (),
        q.rend (),
        event_queue_back_inserter (mainQueue));
  
  p.send ("Event queue ...");
  for_each (ISEQ (q), fShowOneEvent (p));
  
} // end of DoShowEvents

void DoAddSpell (CommandInfo & info)
{

  ObjectList & spells = control ().items_ ["spells"];
  string name = info.wildcards_ ["new_spell"];
  ObjectIter iWhat = FindObject (spells, "name", name);
  if (iWhat != spells.end ())
    throw msg ("spell_exists");
  spells.push_back (Object (name));
    
} // end of DoAddSpell

void DoAddSpellEffect (CommandInfo & info)
{
Object & spell = info.spell ();

  // add a new effect
  spell.contents_.push_back (Object ());
  
  info.player ().send (MAKE_STRING ("You have added effect #" 
                 << spell.contents_.size ()
                 << " to spell " << spell.gets ("name")));

} // end of DoAddSpellEffect

// general field-modifier for any thing
//  - lets you change "type" (s/n/f/b) of field named "field"
//  -  to new value "value"
//    or, drop it if info.wildcards_ ["drop"] == "drop"
void ModifyThing (thing & o, CommandInfo & info)
  {
  Character & p (info.player ());

  if (info.wildcards_ ["type"].empty ())
    throw msg ("no_data_type");
  
  string field = info.wildcards_ ["field"];
  string value = info.wildcards_ ["value"];
  
  // drop the field?
  
  // echo previous value - error if none
  // indicate dropped
  if (tolower (info.wildcards_ ["drop"]) == "drop")
    {
    switch (info.wildcards_ ["type"] [0])
      {
      case 's': if (!o.hass (field))
                  throw msg ("string_field_not_present");
                p.send (MAKE_STRING ("String field " << field << " was = '" << o.gets (field) << "'"));
                o.erases (field); 
                p.send ("String field " + field + " now dropped.");
                break;
      case 'n': if (!o.hasi (field))
                  throw msg ("number_field_not_present");
                p.send (MAKE_STRING ("Number field " << field << " was = " << o.geti (field)));
                o.erasei (field);   
                p.send ("Number field " + field + " now dropped.");
                break;
      case 'f': if (!o.hasf (field))
                  throw msg ("float_field_not_present");
                p.send (MAKE_STRING ("Float field " << field << " was = " << o.getf (field)));
                o.erasef (field);  
                p.send ("Float field " + field + " now dropped.");
                break;
      case 'b': if (!o.hasb (field))
                  throw msg ("bool_field_not_present");
                p.send (MAKE_STRING ("Bool field " << field << " was = " << o.getb (field)));
                o.eraseb (field);  
                p.send ("Bool field " + field + "now dropped.");
                break;
      default:
        throw msg ("bad_data_type");
      
      } // end of switch
    return;
    } // end of dropping it
  
  // echo previous value, if any, and show new value
  switch (info.wildcards_ ["type"] [0])
    {
    case 's': if (o.hass (field))
                p.send (MAKE_STRING ("String field " << field << " was = '" << o.gets (field) << "'"));
              o.sets (field, value);                          
              p.send (MAKE_STRING ("String field " << field << " now = '" << o.gets (field)  << "'"));
              break;
    case 'n': if (o.hasi (field))
                p.send (MAKE_STRING ("Number field " << field << " was = " << o.geti (field)));
              o.seti (field, convertString<int> (value, p.error ("bad_numeric_value")));     
              p.send (MAKE_STRING ("Number field " << field << " now = " << o.geti (field)));
              break;
    case 'f': if (o.hasf (field))
                p.send (MAKE_STRING ("Float field " << field << " was = " << o.getf (field)));
              o.setf (field, convertString<double> (value, p.error ("bad_numeric_value")));  
              p.send (MAKE_STRING ("Float field " << field << " now = " << o.getf (field)));
              break;
    case 'b': if (o.hasb (field))
                p.send (MAKE_STRING ("Bool field " << field << " was = " << o.getb (field)));
              o.setb (field, convertString<bool> (value, p.error ("bad_numeric_value")));      
              p.send (MAKE_STRING ("Bool field " << field << " now = " << o.getb (field)));
              break;
    default:
      throw msg ("bad_data_type");
    
    } // end of switch
   
  } // end of ModifyThing

void DoModifySpellEffect (CommandInfo & info)
{
Character & p (info.player ());
Object & spell = info.spell ();
  
  int number = convertString<int> (info.wildcards_ ["number"], p.error ("bad_numeric_value"));
  // find effect (make number zero-relative)
  ObjectIter iEffect = get_n (ISEQ (spell.contents_), number - 1);
    
  info.sub_ ["name"] = spell;

  if (iEffect == spell.contents_.end ())
    throw msg ("no_effect_number_n");
  
  // change it
  ModifyThing (*iEffect, info);
      
} // end of DoAddSpellEffect

void DoModifySpell (CommandInfo & info)
{
Character & p (info.player ());
Object & spell = info.spell ();
  ModifyThing (spell, info);
} // end of DoModifySpell

void DoDeleteSpell (CommandInfo & info)
{
  ObjectList & spells = control ().items_ ["spells"];
  string name = info.wildcards_ ["spell"];
  ObjectIter iWhat = FindObject (spells, "name", name);
  if (iWhat == spells.end ())
    throw msg ("no_such_spell");
  spells.erase (iWhat); // erase it
    
} // end of DoDeleteSpell

void DoRemoveSpellEffect (CommandInfo & info)
{
Character & p (info.player ());
Object & spell = info.spell ();
  
  int number = convertString<int> (info.wildcards_ ["number"], p.error ("bad_numeric_value"));
  // find effect (make number zero-relative)
  ObjectIter iEffect = get_n (ISEQ (spell.contents_), number - 1);
    
  info.sub_ ["name"] = spell;

  if (iEffect == spell.contents_.end ())
    throw msg ("no_effect_number_n");
  
 
  Object effect = *iEffect;   // get copy
  spell.contents_.erase (iEffect);     // erase it from the spell
  
  p.send (MAKE_STRING ("Removed effect #" << number << "."));
  
} // end of DoRemoveSpellEffect

static int LoadCommandsMap ()
  {
static CommandHandlerMap & commandmap = getCommandHandlerMap ();

  cout << "Loading command handlers in " __FILE__ << endl;
    
  COMMAND (DoClearFlag); // clear a player's flag
  COMMAND (DoClone);     // clone an object
  COMMAND (DoInfo);      // info on a thing
  COMMAND (DoCinfo);     // control info
  COMMAND (DoSetFlag);   // set a player's flag
  COMMAND (DoWriteControl);   // save control file
  COMMAND (DoAddSpell);   // add a spell
  COMMAND (DoDeleteSpell);   // delete a spell
  COMMAND (DoModifySpell);   // modify a spell
  COMMAND (DoAddSpellEffect);   // add an effect to a spell
  COMMAND (DoModifySpellEffect);   // maodify an effect for a spell
  COMMAND (DoRemoveSpellEffect);   // remove an effect from a spell
  COMMAND (DoSpellInfo);   // shows information about a spell
  COMMAND (DoShowSpells);   // shows information about all spells
  COMMAND (DoShowEvents);   // shows event queue

  return 0;
  } // end of LoadCommands

static int loadThemUp = LoadCommandsMap ();
