/*

 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"

//~ attributes used:

//~ PLAYER (number):
  
const string ROOM = "room";     // current room

void CanTakeItem (Character & p, CommandInfo & info, const Object & obj, const bool alreadyHaveIt)
  {
  obj.NeedFlag ("take", "cannot_take");

  info.sub_ ["max_items"].sets ("short", MAKE_STRING (p.geti ("max_items")));
  
  // check item count
  if (p.inventory ().size () >= p.geti ("max_items"))
    throw msg ("cannot_carry_that_many");

  // check weight (if we are not already carrying it)
  if (!alreadyHaveIt)
    {
    if (obj.getiTotal ("weight") + p.getiTotal ("inventory", "weight") >
        p.geti ("max_weight"))
      throw msg ("cannot_carry_that_weight");
    }
  
  } // end of CanTakeItem

void CanDropItem (Character & p,  CommandInfo & info, const Object & obj)
  {
  // put tests here
  } // end of CanDropItem

    
void DoTakeAll (CommandInfo & info)
{
Character & p (info.player ());
  
  if (info.room ().contents ().empty ())
    throw msg ("nothing_to_take");
  
  for (ObjectIter i = info.room ().contents ().begin ();
        i != info.room ().contents ().end ();
        )   // caution - cannot increment iterator here, as moving it changes the list
    {
    // echo item name (no newline)
    p.send (firstLetterCaps (shortDesc (*i, p.prefix ())) + ": ", false);
    info.sub_ ["what"] = *i;
    info.sub_ ["who"].sets ("short", p.message ("you"));
    
    try
      {
      CanTakeItem (p, info, *i, false);  // can we take it?
      moveItem (info.room ().contents (), i++, p.inventory ());
      p.send ("taken", messagemap ());// we took it
      // tell others
      SendToAll ("player_takes_item", messagemap (), info.sub_, &p, NULL, p.geti (ROOM));
      }
    catch (exception & e)
      {
      p.send (e.what (), errormap (), info.sub_);
      //~ p << FixMessage (p.error (), info);   // show error
      ++i;    // next iterator
      }
    } // end of doing each item in the room
} // end of DoTakeAll

void DoTake (CommandInfo & info)
{
Character & p (info.player ());
  
  ObjectList::iterator i = 
    getAnObject (info.wildcards_ ["what"], info.room ().contents (), 
                "take_what", "thing_not_here"  );

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

  moveItem (info.room ().contents (), i, p.inventory ());
    
} // end of DoTake

void DoDrop (CommandInfo & info)
{
Character & p (info.player ());
      
  ObjectIter i = 
    getAnObject (info.wildcards_ ["what"], p.inventory (), 
      "drop_what", "not_in_inventory"  );

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

  CanDropItem (p, info, *i);

  moveItem (p.inventory (), i, info.room ().contents ());
} // end of DoDrop

void DoDropAll (CommandInfo & info)
{
Character & p (info.player ());
  
  if (p.inventory ().empty ())
    throw msg ("not_carrying_anything");
  
  for (ObjectIter i = p.inventory ().begin ();
        i != p.inventory ().end ();
        )   // caution - cannot increment iterator here, as moving it changes the list
    {
    // echo item name (no newline)
    p.send (firstLetterCaps (shortDesc (*i, p.prefix ())) + ": ", false);
    info.sub_ ["what"] = *i;
    try
      {
      info.sub_ ["who"].sets ("short", p.message ("you"));
      CanDropItem (p, info, *i);  // can we drop it?
      moveItem (p.inventory (), i++, info.room ().contents ());
      p.send ("dropped", messagemap ());    // we took it
      // tell others
      SendToAll ("player_drops_item", messagemap (), info.sub_, &p, NULL, p.geti (ROOM));
      }
    catch (exception & e)
      {
      p.send (e.what (), errormap (), info.sub_);
      //~ p << FixMessage (p.error (e.what ()), info);   // show error
      ++i;    // next iterator
      }
    } // end of doing each item in his inventory
} // end of DoDropAll

void DoPut (CommandInfo & info)
{
Character & p (info.player ());
  
  ObjectIter iWhat = 
    getAnObject (info.wildcards_ ["what"], p.inventory (), "put_what", 
        "not_in_inventory"  );

  info.sub_ ["what"] = *iWhat;

  ObjectIter iTo = 
    getAnObject (info.wildcards_ ["container"], p.inventory (), 
        "put_where", "container_not_in_inventory" );

  info.sub_ ["container"] = *iTo;
  
  // we will crash if we put something into itself
  if (iWhat == iTo)
    throw msg ("cannot_put_into_itself");

  if (!iTo->getb ("container"))
    throw msg ("not_a_container");
      
  moveItem (p.inventory (), iWhat, iTo->contents_);
    
} // end of DoPut

// get (thing) from (container)
void DoGet (CommandInfo & info)
{
Character & p (info.player ());
    
  ObjectIter iFrom = 
    getAnObject (info.wildcards_  ["container"], p.inventory (), 
                "get_from_where", "container_not_in_inventory"  );
 
  info.sub_ ["container"] = *iFrom;

  ObjectIter iWhat = findItem (info.wildcards_  ["what"], iFrom->contents_, 
                              "no_thing_in_container");

  info.sub_ ["what"] = *iWhat;
 
  if (!iFrom->getb ("container"))
     throw msg ("not_a_container");

  info.sub_ ["who"].sets ("short", p.message ("you"));
  
  CanTakeItem (p, info, *iWhat, true);  // can we take it (item count limits etc.)?

  moveItem (iFrom->contents_, iWhat, p.inventory ());
    
} // end of DoGet

void DoGive (CommandInfo & info)
{
Character & p (info.player ());
Character & target (info.target ());
      
  ObjectIter i = 
    getAnObject (info.wildcards_ ["what"], p.inventory (), 
      "give_what", "not_in_inventory"  );

  info.sub_ ["what"] = *i;
  info.sub_ ["who"].sets ("short", p.message ("you"));   // who gives it

  CanDropItem (p, info, *i);

  info.sub_ ["who"] = target; // who gets it
  CanTakeItem (target, info, *i);

  moveItem (p.inventory (), i, target.inventory ());
} // end of DoGive

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

  cout << "Loading command handlers in " __FILE__ << endl;
    
  COMMAND (DoDrop);      // drop an object
  COMMAND (DoDropAll);   // drop entire inventory
  COMMAND (DoGet);       // get an object from inside another one
  COMMAND (DoPut);       // put an object into another one
  COMMAND (DoTake);      // take an object
  COMMAND (DoTakeAll);   // take a everything in room
  COMMAND (DoGive);      // give an item to someone

  } // end of LoadCommands

static int loadedCommandsCount = LoadCommandsMap ();
