#ifndef BABBLEMUD_PLAYER_H
#define BABBLEMUD_PLAYER_H

#include <set>
#include <list>
#include <stack>
#include <iostream>   // for cerr in dtor

#ifdef WIN32
  #include <io.h>
#else
  #include <unistd.h>   // for close
#endif

#include "strings.h"  // for ciLess
#include "constants.h"  // for NO_SOCKET
#include "object.h"   // player inventory_
#include "character.h"  // player is based on character
#include "regexp.h"   // for regexps in commands
#include "events.h"

// connection states - add more to have more complex connection dialogs 
typedef enum
{
  eAwaitingLangage,     // we want their language (English, French, Italian etc.)
  eAwaitingName,        // we want their player name
  eAwaitingPassword,    // we want their old password
  
  eAwaitingNewName,     // they have typed 'new' and are being asked for a new name
  eAwaitingNewPassword, // we want a new password
  eConfirmPassword,     // confirm the new password
  eAwaitingClass,       // we want the class name
  
  ePlaying              // this is the normal 'connected' mode
} tConnectionStates;

// Convert colour codes                         
const string ConvertColour (const string & source, 
                            stack<string> & colourStack, // for push/pop
                            string & currentColour,      // for stacking
                            const bool wantAnsi = true);

/*---------------------------------------------- */
/*  player class - holds details about each connected player */
/*---------------------------------------------- */

class Player : public Character
{

private:
  
// these fields here are only relevant for real players connected
// via the Internet

  int s_;              // socket they connected on
 
  string outbuf_;      // pending output
  string inbuf_;       // pending input

  // stuff for colour output, to make it more efficient
  stack<string> colourStack; // for push/pop
  string currentColour;      // last colour sequence output

  time_t lastCommandTime_;    // for timeout checks
  int    commandsThisSecond_; // commands entered this second

// copy construction is probably an error.
// Beware of:     Character   p (info.player ());  <-- copy constructed
//   rather than: Character & p (info.player ());  <-- just get a reference

  Player (const Player &);  // do not allow copy construction!!!

public:
  
  tConnectionStates connstate_;   // connection state
  int badPasswordCount_;          // password guessing attempts
  string address_;                // IP address they came from
  bool wantAnsi;                  // want ANSI colour codes?
  ThingMap sub_;                 // substitutions for errors during connection phase
                                  // eg. bad vnum - what vnum?

  // this is not a mob
  virtual const bool mob () const { return false; }

  // ctor
  Player (const int sock) : s_ (sock), wantAnsi (true)  
      { 
      Init (); 
      } 
  
  virtual ~Player (); // dtor
  void Init ();
    
  // output routines are overridden as mobs
  // will handle output differently, if at all
    
 // send a message which has been decoded
  virtual void send (const string & msg, const bool newline = true);
  // send a message code - lookup in whichmap
  virtual void send (const string & code, const StringMap & whichmap, const bool newline = true);
  // send a decoded message, substitute with subs
  virtual void send (const string & msg, const ThingMap & subs, const bool newline = true);
  // send a message code - lookup in whichmap, substitute with subs
  virtual void send (const string & code, const StringMap & whichmap, 
                     const ThingMap & subs, const bool newline = true, const bool notFoundIsOk = false);
    
  //~ virtual Player & operator<< (const string & s)  
    //~ {     
    //~ outbuf_ += ConvertColour (s, colourStack, currentColour, wantAnsi); 
    //~ return *this; 
    //~ };  
 
  // what's our socket?
  int GetSocket () const { return s_; }
  // true if connected at all
  bool Connected () const { return s_ != NO_SOCKET; }
  // true if this player actively playing
  bool IsPlaying () const { return Connected () && connstate_ == ePlaying && !closing_; }
  // true if we have something to send them
  bool PendingOutput () const { return !outbuf_.empty (); }
  // when did they last type something?
  const time_t GetLastCommandTime () const { return lastCommandTime_; }
  // how many commands did they type this second?
  const int GetCommandsThisSecond () const { return commandsThisSecond_; }

  // TCP/IP read/write/exception routines
  void ProcessRead ();    // get player input
  void ProcessWrite ();   // output outstanding text
  void ProcessException (); // exception on socket

  // load/save player
  void Load ();           // load player from disk
  virtual void Save ();           // save player to disk
    
};  // end of class Player 
  
// player list type
typedef std::list <Player*> PlayerList;
typedef PlayerList::iterator PlayerListIterator;

// an action handler (commands, connection states, events)
typedef void (*tCommandHandler) (CommandInfo & info);
typedef void (*tStateHandler)   (Player * p, istream & sArgs);
typedef void (*tEventHandler)   (Event & e);

// maps of handlers
typedef map<string, tCommandHandler> CommandHandlerMap;
typedef map<tConnectionStates, tStateHandler> StateHandlerMap;
typedef map<string, tEventHandler> EventHandlerMap;

typedef map<string, int> StringIntMap;

// find a player or mob by name (if inRoom != 0, only in that room)
Character * FindPlayer (const string & name, const int inRoom = 0);
// find a character by ID
Character * FindPlayerID (const string &id);

void ProcessCommand (Character * p, istream & sArgs, const bool forced = false);
void ProcessCommand (Player * p, istream & sArgs);
void ProcessPlayerInput (Player * p, const string & s);
void SendToAll (const string & code,              // code, eg "not_permitted"
                const StringMap & whichmap,       // where to look code up
                const ThingMap & subs = ThingMap (),    // substitutions (eg. <target>
                const Character * Except1 = NULL,  // first exception (eg. player)
                const Character * Except2 = NULL,  // second exception (eg. target)
                const int InRoom = 0,              // room 
                const bool notFoundIsOk = false);   // OK if no message code found?

void SendToAll (const string & message,     // what to send
                const string & flag);       // flag must be set

ObjectList::const_iterator getAnObject (const string & name,  // what they typed
                                       const ObjectList & contents,   // where to look (eg. room, player)
                                       const string & emptymessage,  // if nothing requested
                                       const string & notfoundmessage); // if not there

ObjectList::iterator getAnObject (const string & name,  // what they typed
                                 ObjectList & contents,   // where to look (eg. room, player)
                                 const string & emptymessage,  // if nothing requested
                                 const string & notfoundmessage); // if not there

void CanTakeItem (Character & p, CommandInfo & info, const Object & obj, const bool alreadyHaveIt = false);
void CanDropItem (Character & p, CommandInfo & info, const Object & obj);

const string FixMessage (const string & prefix,
                         const string & source, 
                         const ThingMap & subs,
                         const bool addNewline = true);
void warnAdmin (const string & msg);

// for generating command handlers
#define COMMAND(arg) commandmap [#arg] = arg

// for generating event handlers
#define EVENT(arg) eventmap [#arg] = arg

#endif // BABBLEMUD_PLAYER_H
