/*

ARIA header files for use with ARNL 1.7.1

Copyright(C) 2004, 2005 ActivMedia Robotics, LLC. 
Copyright(C) 2006, 2007, 2008, 2009 MobileRobots Inc.
All rights reserved.

This copy of Aria was relicensed for use with Arnl and the Arnl
license by MobileRobots Inc.  If you wish to download a seperate
distribution of Aria licensed under the GPL or a commercial license go to
http://www.mobilerobots.com/SOFTWARE/aria.html or contact MobileRobots
Inc, at robots@mobilerobots.com or MobileRobots Inc,
10 Columbia Drive, Amherst, NH 03031; 800-639-9481

MobileRobots Inc hereby grants to other individuals or
organizations permission to use this software with Arnl and in
compliance with the Arnl license.  This software may not be
distributed to others except by MobileRobots Inc.

MobileRobots Inc does not make any representations about the
suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.

*/
#ifndef ARSERVERMODE_H
#define ARSERVERMODE_H

#include "Aria.h"
#include "ArServerBase.h"

class ArServerModeIdle;
class ArServerClient;

/// A mode for controlling the robot (only one active)

/**
   This is a lot like ArMode but for net control instead...

   Only one mode can control the robot at once, this is made to be
   subclassed.  Each subclass needs to implement activate and
   deactivate, activate MUST call baseActivate and if baseActivate
   returns false then the mode is NOT active and shouldn't do
   anything.  Further each class should set myMode and myStatus to the
   mode its in and the things its doing... You can lock your mode in
   with lockMode and unlock it with unlockMode but you should redefine
   the inherited requestUnlock and unlock yourself and deactivate
   gracefully.  

   If you want to implement a server mode as a default mode then you also
   need to implement checkDefault which will be called when nothing
   else is active

   Note that there's a new mechanism in place where if you use
   addModeData instead of addData on the ArServerBase the command will
   be placed into a map of available modes with commands that can be
   gotten by the client.  The active mode and whether its locked and
   will unlock and such can now be gotten too... this is all so that
   different commands can be disabled in client GUIs when they aren't
   available.
**/
class ArServerMode
{
public:
  /// Constructor
  AREXPORT ArServerMode(ArRobot *robot, ArServerBase *server, const char *name);
  /// Destructor
  AREXPORT virtual ~ArServerMode();
  /// This function should return the action group this mode uses
  AREXPORT virtual ArActionGroup *getActionGroup(void) { return NULL; }
  /** The function called when the mode is activated. Subclasses must implement
    * this, but also call baseActivate().-
    */
  AREXPORT virtual void activate(void) = 0;
  /** The function called when the mode is deactivated. Subclasses must
   * implement this, and also call baseDeactivate().
   */
  AREXPORT virtual void deactivate(void) = 0;
  /// The function called if the mode is not activated because another mode superceded it.
  AREXPORT virtual void activationDenied(void) {}

  /// The ArMode's user task, don't need one, subclass must provide if needed
  AREXPORT virtual void userTask(void) {}
  /// This will be called if another mode wants the lock broken (can ignore)
  AREXPORT virtual void requestUnlock(void) { }
  /// This will be called if another mode has to break the lock
  AREXPORT virtual void forceUnlock(void);
  /// Locks this mode in until its unlocked (mode must be the active mode)
  AREXPORT void lockMode(bool willUnlockIfRequested = false);
  /// Unlocks the mode so other modes can activate
  AREXPORT void unlockMode(void);      
  /// Gets a string representing the mode we're in
  AREXPORT const char *getMode(void) const { return myMode.c_str(); }
  /// Gets a string representing the status of the mode we're in
  AREXPORT const char *getStatus(void) const { return myStatus.c_str(); }
  /// Gets the name of the mode we're in
  AREXPORT const char *getName(void) const { return myName.c_str(); }
  /// Gets if this mode is active or not
  AREXPORT bool isActive(void) const { return myIsActive; }
  /// Gets whether we'll unlock if requested or not
  AREXPORT static bool willUnlockIfRequested(void);
  /// Gets the active mode
  AREXPORT static ArServerMode *getActiveMode(void);
  /// Gets if we've set our activity time
  AREXPORT bool hasSetActivityTime(void) { return myHasSetActivityTime; }
  /// Gets the time of our last activity 
  AREXPORT ArTime getActivityTime(void);
  /// Sets that we're active right now
  AREXPORT void setActivityTimeToNow(void);
  /// Gets the seconds since the activity of our active mode
  AREXPORT static int getActiveModeActivityTimeSecSince(void);
  /// Sets this mode to default (so if a mode deactivates it activates this)
  AREXPORT void addAsDefaultMode(ArListPos::Pos pos = ArListPos::LAST);
  /// Sees if this wants to become the default mode
  /**
     This will be called when there would be no other modes, if this
     mode wants to take over it should activate itself, if it doesn't
     want to take over it shouldn't activate
   **/
  AREXPORT virtual void checkDefault(void) {}
  /// This should only be used by careful people and probably not then
  AREXPORT void setMode(const char *str) { myMode = str; }
  /// This should only be used by careful people and probably not then
  /**
     This changes the status of the mode and sets the
     myStatusSetThisCycle to true.  If the mode inheriting is setting
     the status in cycles then it should use this status instead and
     then clear the myStatusSetThisFlag to false.
   **/
  AREXPORT void setStatus(const char *str) 
    { myStatus = str; myStatusSetThisCycle = true; }
  /// Adds data to the list of this mode's commands
  
 
  /// Returns whether the mode should be automatically resumed after it was interrupted
  /**
   * This method primarily applies when the mode has been interrupted by a 
   * forced docking situation.  In that case, when the docking mode is complete,
   * it will resume the interrupted mode -- if this method returns true.  This
   * would primarily be applicable to very long, complex modes.
   *
   * The default return value is false.
  **/
  AREXPORT virtual bool isAutoResumeAfterInterrupt();
 

  AREXPORT bool addModeData(
	  const char *name, const char *description,
	  ArFunctor2<ArServerClient *, ArNetPacket *> *functor,
	  const char *argumentDescription, const char *returnDescription,
	  const char *commandGroup = NULL, const char *dataFlags = NULL);
  /// Gets the list of data for each mode
  AREXPORT static void getModeDataList(ArServerClient *client, 
				       ArNetPacket *packet);
  /// Gets the info about which mode is active and such
  AREXPORT static void getModeInfo(ArServerClient *client, 
				   ArNetPacket *packet);
  /// Handles the packet request for the mode busy state.
  AREXPORT static void getModeBusy(ArServerClient *client,
                                   ArNetPacket *packet);
  
  /// Adds a callback for when this class is activated
  void addActivateCallback(
	  ArFunctor *functor, int position = 50) 
    { myActivateCallbacks.addCallback(functor, position); }
  /// Removes a callback for when this class is activated
  void remActivateCallback(ArFunctor *functor)
    { myActivateCallbacks.remCallback(functor); }
  /// Adds a callback for when this class is deactivated
  void addDeactivateCallback(
	  ArFunctor *functor, int position = 50) 
    { myDeactivateCallbacks.addCallback(functor, position); }
  /// Removes a callback for when this class is deactivated
  void remDeactivateCallback(ArFunctor *functor)
    { myDeactivateCallbacks.remCallback(functor); }

  /// Adds a single shot callback for when this class is deactivated
  void addSingleShotDeactivateCallback(
	  ArFunctor *functor, int position = 50) 
    { mySingleShotDeactivateCallbacks.addCallback(functor, position); }
  /// Removes a single shot callback for when this class is deactivated
  void remSingleShotDeactivateCallback(ArFunctor *functor)
    { mySingleShotDeactivateCallbacks.remCallback(functor); }

  /// Call that gets our idle mode
  AREXPORT static ArServerModeIdle *getIdleMode(void);
protected:
  AREXPORT static void modeUserTask(void);
  AREXPORT static void buildModeInfoPacket(ArNetPacket *packet);
  AREXPORT static std::list<ArServerMode *> *getRequestedActivateModes(void);
  AREXPORT void checkBroadcastModeInfoPacket(void);
  /** Activates this mode if it can (returns true if it can, false otherwise).
      If we have an ArRobot instance (myRobot), then robot motion is stopped.
      Activity time is reset, and activate callbacks are called.
  */
  AREXPORT bool baseActivate(void);
  /** Deactivates this mode. Deactivation callbacks are called. The next mode to
      be activated is activated, if any. (E.g. the global default mode)
  */
  AREXPORT void baseDeactivate(void);

  ArCallbackList myActivateCallbacks;
  ArCallbackList myDeactivateCallbacks;
  ArCallbackList mySingleShotDeactivateCallbacks;

  bool myIsActive;
  ArRobot *myRobot;
  ArServerBase *myServer;
  std::string myName;
  // inheritors will want to play with these two strings as they are
  // what is shown to the user
  std::string myMode;
  std::string myStatus;
  bool myStatusSetThisCycle;

  // variables for activity time (so we can do things if idle) this
  // isn't static so that each mode can have its own
  // 'myHasSetActivityTime' so that if a mode doesn't use this scheme
  // it doesn't get tromped on
  bool myHasSetActivityTime;
  ArTime myActivityTime;
  ArMutex myActivityTimeMutex;
  
  AREXPORT static bool ourActiveModeLocked;
  AREXPORT static bool ourActiveModeWillUnlockIfRequested;
  AREXPORT static ArServerMode *ourActiveMode;
  AREXPORT static ArServerMode *ourNextActiveMode;
  AREXPORT static ArServerMode *ourLastActiveMode;

  AREXPORT ArServerMode *getLastActiveMode();

  AREXPORT static bool ourIsBusy;
  AREXPORT static ArServerBase *ourServerBase;

  AREXPORT static std::list<ArServerMode *> ourModes;
  AREXPORT static std::list<ArServerMode *> ourDefaultModes;
  AREXPORT static std::list<ArServerMode *> ourRequestedActivateModes;
  AREXPORT static std::multimap<std::string, std::string> ourModeDataMap;
  AREXPORT static ArMutex ourModeDataMapMutex;
  
  AREXPORT static ArGlobalFunctor2<ArServerClient *, 
				   ArNetPacket *> ourGetModeDataListCB;
  AREXPORT static ArGlobalFunctor2<ArServerClient *, 
				   ArNetPacket *> ourGetModeInfoCB;
  AREXPORT static ArGlobalFunctor2<ArServerClient *, 
				   ArNetPacket *> ourGetModeBusyCB;

  AREXPORT static bool ourBroadcastActiveModeLocked;
  AREXPORT static ArServerMode *ourBroadcastActiveMode;
  AREXPORT static bool ourBroadcastActiveModeWillUnlockIfRequested;
  AREXPORT static ArGlobalFunctor ourUserTaskCB;    
  AREXPORT static bool ourUserTaskAdded;  
  // so we can easily just get the verbose out of this one
  ArLog::LogLevel myVerboseLogLevel;

  AREXPORT static ArServerModeIdle *ourIdleMode;
  AREXPORT static ArMutex ourIdleModeMutex;
  AREXPORT static bool ourIdleModeCreated;
};

#endif // ARSERVERMODE_H
