/*!
  \file

  This is the main header file for the SciGPU::Legion library.
*/

namespace SciGPU {
  namespace Legion {
    /*!
      \mainpage The SciGPU::Legion Library
      
      This library is for managing a group of execution resources
      (such as CPU coress, CUDA GPUs etc.).
      Individual resources are controlled by
      \ref Maniple "Maniples" which are grouped into a Legion.
      The user can then submit \ref Task "Tasks" to the Legion.
      The \ref Maniple "Maniples" monitor the list of submitted
      \ref Task "Tasks" and select appropriate \ref Task "Tasks"
      to perform.


      \section require Requirements

      The SciGPU::Legion Library is build on top of boost::thread.
      The build system makes use of CMake.

      \section tutorial Tutorial

      There are a number of tutorial pages
      <ol>
      <li>\ref hello</li>
      <li>\ref basicwork</li>
      <li>\ref multitask</li>
      <li>\ref picky</li>
      <li>\ref hellocuda</li>
      <li>\ref cudapinnedmem</li>
      </ol>

    */
  }
}


#ifndef SCIGPU_LEGION_HPP
#define SCIGPU_LEGION_HPP


#include <string>
#include <list>
#include <map>
#include <set>
#include <iostream>

#include <boost/thread/thread.hpp>

//! Namespace for SciGPU projects
namespace SciGPU {

  //! Namespace to contain members of the Legion library
  namespace Legion {

    // Forward declarations
    class Task;
    class Maniple;
    class Legion;

    // Type definitions

    //! Defines the type which users can use to tag Task objects
    typedef unsigned long TagType;

    //! Defines a map  to count tasks
    typedef std::map<TagType,unsigned int> TaskCount;

    //! A map to monitor tasks performed by \ref Maniple "Maniples"
    typedef std::map<boost::thread::id,TaskCount> ManipleStats;

    //! A set for listing Task::tag
    typedef std::set<TagType> TagSet;

    //! Defines a list of pointers to \ref Task "Tasks"
    typedef std::list<Task*> TaskList;

    //! Piggy-back ManipleIDs onto boost
    typedef boost::thread::id ManipleID;

    // ###############################################################

    //! Abstract base class to describe a task to be performed by a Legion
    /*!
      A Task is the basic unit of computation performed by a Legion
      of \ref Maniple "Maniples".
      This is an abstract base class, which provides a minimal description
      of a unit of computation.
      The user must derive classes from the Task class, which override the
      pure virtual function Task::Perform.
      
      When a Maniple acquires a Task object, it calls the Task::Perform
      method, in order to perform the required computation.
      In addition, the Maniple will set the Task::maniple pointer, so that
      the Task can access the public methods of the Maniple.
      The only useful method of the basic Maniple class is
      Maniple::LogMessage, which will write a string to the clog stream.
      The Maniple will also set the Task::thread member, to identify the
      thread it controls.
      
      This class contains a Task::tag member which can be used by
      user code.
      It is not examined by any components of the SciGPU::Legion library,
      except for the purposes of generating statistics
    */
    class Task {

      friend class Maniple;

    public:
      //! Constructor
      Task( TagType _tag=0 ) : tag(_tag),
			       thread(),
			       maniple(NULL) {};
      
      //! Destructor
      virtual ~Task( void ) {};

      //! Method to perform the task
      /*!
	This is a pure virtual function, which must be over-ridden by
	users, in order to perform the required work.
	@retval bool Whether the task completed successfully
      */
      virtual bool Perform( void ) = 0;
      
      //! Tag for user code
      TagType tag;

      //! Accessor method for Maniple which processed this task
      const ManipleID GetManipleID( void ) const {
	return( this->thread );
      }

    protected:
      //! Maniple  which processed this task
      ManipleID thread;

      //! The maniple running this Task
      Maniple* maniple;
    };
    

    // ###############################################################

    //! Class which can perform \ref Task "Tasks" for a Legion
    /*!
      The Maniple class is a 'worker' class for the Legion.
      The \ref Maniple "Maniples" of a Legion select
      \ref Task "Tasks" from the 'pending' list of their
      parent Legion, and perform them.
      The Maniple class itself is an abstract base class,
      since it does not define the Maniple::ApproveType
      method.
      This method must be over-ridden by child classes,
      to test individual Task objects, to see if they are
      of an appropriate type.
    */
    class Maniple {
      friend class Legion;

    public:

      //! Constructor
      Maniple( void ) : legion(NULL) {};

      //! Destructor
      virtual ~Maniple( void ) {};

      //! Method called by boost on thread creation
      void operator()( void );

      //! Method to print message to clog, using Legion's I/O mutex
      void LogMessage( const std::string& msg );

    protected:

      //! Method to perform any required initialisation
      virtual void Initialise( void ) {};

      //! Method to perform any required finalisation
      virtual void Finalise( void ) {};

      //! Method to select a Task for the Maniple from a range
      virtual TaskList::const_iterator
      SelectTaskFromRange( const TaskList::const_iterator& begin,
			   const TaskList::const_iterator& end );

      //! Method to approve the type of a Task object for a Maniple
      virtual bool ApproveType( const Task& theTask ) = 0;

      //! Method to approve a Task for a Maniple
      virtual bool ApproveTask( const Task& theTask );

      //! Templated method to check Task types
      template<typename T>
      bool CheckTaskType( const Task& theTask ) {
	bool canCast;

	try {
	  dynamic_cast<const T&>(theTask);
	  
	  canCast = true;
	}
	catch( std::bad_cast ) {
	  canCast = false;
	}

	return( canCast );
      }


    private:
      //! Method to fetch a task
      Task* FetchTask( void );
      
      //! Pointer to the parent Legion
      Legion* legion;
    };

    // ###############################################################

    //! A group of execution resources which can perform a Task list
    /*!
      A Legion is a group of Maniple objects which can perform the work
      described by Task objects.
      The user enqueues \ref Task "Tasks" which the Legion then performs
      using is constitute \ref Maniple "Maniples".
    */
    class Legion {
      friend class Maniple;

    public:
      //! Constructor
      Legion( void ) : manipleWait(100),
		       pool(),
		       io_mutex(),
		       pending(), pendingMutex(), pendingCond(),
		       complete(), completeMutex(), completeCond(),
		       seenTags(), seenMutex(),
		       compStats(), compStatsMutex() {} ;

      //! Destructor
      ~Legion( void ) {
	this->Disband();
      };


      //! Method to add a maniple
      template<typename T>
      ManipleID AddManiple( T maniple ) {
	
	maniple.legion = this;
	boost::thread* thd = this->pool.create_thread( maniple );

	return( thd->get_id() );
      }
      
      //! Method to terminate all maniples
      void Disband( void ) {
	this->pool.interrupt_all();
	this->pool.join_all();
      }


      //! Adds a task to the 'pending' list
      void Enqueue( Task* const theTask );

      //! Collects a completed task
      Task* CollectOne( void );

      //! Returns the number of pending tasks
      const size_t CountPending( void );

      //! Returns the number of complete tasks
      const size_t CountComplete( void );

      //! Returns the set of tags seen by the Legion
      void SeenTags( TagSet& seen );

      //! Returns the statistics of completed \ref Task "Tasks"
      void CompletedStatistics( ManipleStats& cStats );

      //! Prints the 'completed' statistics to an ostream
      void PrintCompletedStats( std::ostream& os );

      //! Microseconds a Maniple sleeps when it fails to get a Task
      boost::posix_time::microseconds manipleWait;

    private:

      //! The pool of threads managed by this Legion
      boost::thread_group pool;

      //! Mutex to control I/O from this Legion
      boost::mutex io_mutex;

      //! The list of pending tasks
      TaskList pending;
      //! Mutex to control access to the pending list
      boost::mutex pendingMutex;
      //! Condition variable for the pending list
      boost::condition_variable pendingCond;

      //! The list of completed work units
      std::list<Task*> complete;
      //! Mutex to control access to the completed tasks
      boost::mutex completeMutex;
      //! Condition variable for the completed tasks
      boost::condition_variable completeCond;

      //! The set of Task::tag values see
      TagSet seenTags;
      //! Mutex to protect seenTags
      boost::mutex seenMutex;

      //! Counts of completed Task objects by Maniple
      ManipleStats compStats;
      //! Mutex to protect compStats;
      boost::mutex compStatsMutex;

      //! Pretty prints the given tags and statistics to an ostream
      void PrintStatistics( std::ostream& os,
			    const TagSet& tags,
			    ManipleStats& stats );
    };

  }
}


#endif
