/*!
  \file
  File containing method definitions for the Maniple class.
*/
#include <iostream>
#include <iterator>
using namespace std;

#include "legion.hpp"



namespace SciGPU {

  namespace Legion {

    // -------------------------------------------------------

    void Maniple::operator()( void ) {

      this->LogMessage( "Starting up" );
     
      // Perform any required initialisation
      this->Initialise();

      try {

	do {
	  // Get the next task
	  Task* myTask = this->FetchTask();

	  // Notify waiting threads
	  this->legion->pendingCond.notify_one();

	  // If myTask is NULL, we didn't find a task
	  if( myTask != NULL ) {

	    // Stamp task with our thread ID
	    myTask->thread = boost::this_thread::get_id();

	    // Grant the task access to this maniple
	    myTask->maniple = this;

	    // Do the task
	    bool taskSuccess = myTask->Perform();

	    // Remove task access to the maniple
	    myTask->maniple = NULL;

	    if( taskSuccess ) {
	      // Put onto the list of completed tasks
	      {
		boost::mutex::scoped_lock lock( this->legion->completeMutex );
		this->legion->complete.push_back(myTask);
	      }
	      this->legion->completeCond.notify_one();

	      /*
		Add on to the statisics.
		map::operator[] will implicitly create a new key if necessary.
		I'm relying on integer values being initialised to zero
	      */
	      boost::mutex::scoped_lock lock( this->legion->compStatsMutex );
	      this->legion->compStats[ boost::this_thread::get_id() ] [ myTask->tag ] += 1;

	    } else {
	      cerr << __FUNCTION__ << ": Failure not yet implemented!" << endl;
	      abort();
	    }

	  }  else {
	    /*
	      We didn't get a task, so sleep for a short
	      duration, to give other threads a chance
	    */
	    boost::this_thread::sleep( this->legion->manipleWait );
	  }

	} while( true );
      }
      catch( boost::thread_interrupted ) {
	this->LogMessage( "Caught terminate signal" );
      }

      // Perform any required finalisation
      this->Finalise();

      this->LogMessage( "Shutting down" );
    }

    // -------------------------------------------------------
    
    void Maniple::LogMessage( const std::string& msg ) {
      boost::mutex::scoped_lock lock( this->legion->io_mutex );
      clog << "Maniple " << boost::this_thread::get_id()
	   << ": " << msg << endl;
    }


    // -------------------------------------------------------
    
    bool Maniple::ApproveTask( const Task& theTask ) {
      /*!
	This over-rideable method examines a given Task
	to determine if the Maniple will accept it.
	This basic version always approves a Task.
      */
      return( true );
    }

    // -------------------------------------------------------

    TaskList::const_iterator
    Maniple::SelectTaskFromRange( const TaskList::const_iterator& begin,
				  const TaskList::const_iterator& end ) {
      /*!
	This over-rideable method selects the next task
	from those in the range [\a begin, \a end ).
	If it is unable to find a suitable element, it should
	return \a end.
	By default, it loops between the iterators, looking
	for items which satisfy the (over-ridden)
	Maniple::ApproveType method.
	If it is of an appropriate type, the Maniple::ApproveTask method
	is used to determine whether this particular Task should
	be taken.
	The default method for Maniple::ApproveTask always returns
	\c true.
      */
      TaskList::const_iterator it, res;

      res = end;
      for( it=begin; it!=end; it++ ) {

	// Check the type of the task
	if( this->ApproveType( *(*it) )  ) {
	
	  // Check the task itself
	  if( this->ApproveTask( *(*it) ) ) {
	    res = it;
	    break;
	  }
	  
	}
      }

      return( res );
    }

    // -------------------------------------------------------

    Task* Maniple::FetchTask( void ) {
       /*!
	This method is responsible for fetching the
	next task to be processed by the Maniple.
	It will return NULL if no suitable task is
	available.
	It uses the (over-ridden) SelectTaskFromRange() method
	to pick the task for processing.
      */

      // By default, we select nothing
      Task* res = NULL;

      // Lock the 'pending' list
      boost::mutex::scoped_lock lock( this->legion->pendingMutex );


      // Check if the size is zero
      while( this->legion->pending.size() == 0 ) {
	this->legion->pendingCond.wait( lock );
      }


      // At this point, we will have a lock on the pendingMutex


      // Next, we have to pick the next task

      // Some shorthand
      typedef TaskList::const_iterator ListTci;
      typedef TaskList::iterator ListTi;

      ListTci ticSelect = this->SelectTaskFromRange( this->legion->pending.begin(),
						     this->legion->pending.end() );

      /*
	Some acrobatics are required here, since the SelectTask
	method returns a const_iterator.
	It has to do this, since it operates between
	const_iterators.
	I do this to prevent user-supplied SelectTask methods
	messing up the pending list.
	However, the FetchTask method erases the selected task
	from the pending list, so we have to turn this
	const_iterator back into a normal one.
	This is a non-trivial task, and it seems that the
	solution is to use the STL 'advance' routine to
	move a normal iterator from the beginning of the list
	to the location of the const_iterator
      */


      /*
	Check if we were handed the 'end'
	Note that the check is made against the const_iterator,
	which probably tells us something about iterator
	equality
      */
      if( ticSelect != this->legion->pending.end() ) {

	ListTi taskIt = this->legion->pending.begin();

	// Advance up to the 'real' element

	int dst;
	dst = distance<ListTci>( this->legion->pending.begin(),
				 ticSelect );


	advance( taskIt, dst );

	// Assign
	res = *taskIt;

	// Delete from the pending list
	this->legion->pending.erase( taskIt );
      }

      // On return, the 'pending' mutex will unlock
      return( res );
    }

  }
}
