/*

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 ARTHREAD_H
#define ARTHREAD_H


#include <map>
#ifndef WIN32
#include <pthread.h>
#endif
#include "ariaTypedefs.h"
#include "ArMutex.h"
#include "ArFunctor.h"
#include "ArLog.h"

/// POSIX/WIN32 thread wrapper class. 
/**
  create() will create the thread. That thread will run the given Functor.

  A thread can either be in a detached state or a joinable state. If the
  thread is in a detached state, that thread can not be join()'ed upon. The
  thread will simply run until the program exits, or its function exits.
  A joinable thread means that another thread and call join() upon it. If
  this function is called, the caller will block until the thread exits
  its function. This gives a way to synchronize upon the lifespan of threads.

  Calling cancel() will cancel the thread.

  The static function self() will return a thread
*/
class ArThread
{
public:

#ifdef WIN32
  typedef DWORD ThreadType;
#else
  typedef pthread_t ThreadType;
#endif

  typedef std::map<ThreadType, ArThread*> MapType;
  typedef enum {
    STATUS_FAILED=1, ///< Failed to create the thread
    STATUS_NORESOURCE, ///< Not enough system resources to create the thread
    STATUS_NO_SUCH_THREAD, ///< The thread can no longer be found
    STATUS_INVALID, ///< Thread is detached or another thread is joining on it
    STATUS_JOIN_SELF, ///< Thread is your own thread. Can't join on self.
    STATUS_ALREADY_DETATCHED ///< Thread is already detatched
  } Status;

  /// Constructor
  AREXPORT ArThread(bool blockAllSignals=true);
  /// Constructor - starts the thread
  AREXPORT ArThread(ThreadType thread, bool joinable,
		    bool blockAllSignals=true);
  /// Constructor - starts the thread
  AREXPORT ArThread(ArFunctor *func, bool joinable=true,
		    bool blockAllSignals=true);
  /// Destructor
  AREXPORT virtual ~ArThread();

  /// Initialize the internal book keeping structures
  AREXPORT static void init(void);
  /// Returns the instance of your own thread (the current one)
  AREXPORT static ArThread * self(void);
  /// Returns the os self of the current thread
  AREXPORT static ThreadType osSelf(void);
  /// Stop all threads
  AREXPORT static void stopAll();
  /// Cancel all threads
  AREXPORT static void cancelAll(void);
  /// Join on all threads
  AREXPORT static void joinAll(void);
  /// Yield the processor to another thread
  AREXPORT static void yieldProcessor(void);
  /// Gets the logging level for thread information
  static ArLog::LogLevel getLogLevel(void) { return ourLogLevel; }
  /// Sets the logging level for thread information
  static void setLogLevel(ArLog::LogLevel level) { ourLogLevel = level; }

  /// Create and start the thread
  AREXPORT virtual int create(ArFunctor *func, bool joinable=true,
			      bool lowerPriority=true);
  /// Stop the thread
  virtual void stopRunning(void) {myRunning=false;}
  /// Join on the thread
  AREXPORT virtual int join(void **ret=NULL);
  /// Detatch the thread so it cant be joined
  AREXPORT virtual int detach(void);
  /// Cancel the thread
  AREXPORT virtual void cancel(void);

  /// Get the running status of the thread
  virtual bool getRunning(void) const {return(myRunning);}
  /// Get the running status of the thread, locking around the variable
  virtual bool getRunningWithLock(void) 
    { bool running; lock(); running = myRunning; unlock(); return running; }
  /// Get the joinable status of the thread
  virtual bool getJoinable(void) const {return(myJoinable);}
  /// Get the underlying thread type
  virtual const ThreadType * getThread(void) const {return(&myThread);}
  /// Get the underlying os thread type
  virtual ThreadType getOSThread(void) const {return(myThread);}
  /// Get the functor that the thread runs
  virtual ArFunctor * getFunc(void) const {return(myFunc);}

  /// Set the running value on the thread
  virtual void setRunning(bool running) {myRunning=running;}

#ifndef SWIG
  /** Lock the thread instance
      @swigomit
  */
  int lock(void) {return(myMutex.lock());}
  /** Try to lock the thread instance without blocking
      @swigomit
  */
  int tryLock(void) {return(myMutex.tryLock());}
  /** Unlock the thread instance
      @swigomit
  */
  int unlock(void) {return(myMutex.unlock());}
#endif

  /// Do we block all process signals at startup?
  bool getBlockAllSignals(void) {return(myBlockAllSignals);}

  /// Gets the name of the thread
  virtual const char *getThreadName(void) { return myName.c_str();  }

  /// Sets the name of the thread
  AREXPORT virtual void setThreadName(const char *name);

  /// Gets a string that describes what the thread is doing NULL if it
  /// doesn't know
  virtual const char *getThreadActivity(void) { return NULL; }

  /// Marks the thread as started and logs useful debugging information.
  /**
     If you call this function in your functor (ie runThread) it'll
     then call some things for logging (to make debugging easier)
     This method should be called before the main thread loop begins.
   **/
  AREXPORT virtual void threadStarted(void);

  /// Marks the thread as finished and logs useful debugging information.
  /**
     This method should be called after the main thread loop ends.  It
     enables the creator of the thread to determine that the thread has
     actually been completed and can be deleted.
   **/
  AREXPORT virtual void threadFinished(void);

  /// Returns whether the thread has been started.
  /**
   * This is dependent on the thread implementation calling the 
   * threadStarted() method.
  **/
  AREXPORT virtual bool isThreadStarted() const;

  /// Returns whether the thread has been completed and can be deleted.
  /**
   * This is dependent on the thread implementation calling the 
   * threadFinished() method.
  **/
  AREXPORT virtual bool isThreadFinished() const;


  /// Logs the information about this thread
  AREXPORT virtual void logThreadInfo(void);

#ifndef WIN32
  pid_t getPID(void) { return myPID; }
#endif

  /// Gets the name of the this thread
  AREXPORT static const char *getThisThreadName(void);
  /// Get the underlying thread type of this thread
  AREXPORT static const ThreadType * getThisThread(void);
  /// Get the underlying os thread type of this thread
  AREXPORT static ThreadType getThisOSThread(void);

protected:
  static ArMutex ourThreadsMutex;
  static MapType ourThreads;
#ifdef WIN32
  static std::map<HANDLE, ArThread *> ourThreadHandles;
#endif 
  AREXPORT static ArLog::LogLevel ourLogLevel; 

  AREXPORT virtual int doJoin(void **ret=NULL);

  std::string myName;

  ArMutex myMutex;
  /// State variable to denote when the thread should continue or exit
  bool myRunning;
  bool myJoinable;
  bool myBlockAllSignals;

  bool myStarted;
  bool myFinished;

  ArStrMap myStrMap;
  ArFunctor *myFunc;
  ThreadType myThread;
#ifdef WIN32
  HANDLE myThreadHandle;
#endif

  
#ifndef WIN32
  pid_t myPID;
#endif

  static std::string ourUnknownThreadName;
};


#endif // ARTHREAD_H
