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

#include "legion.hpp"



namespace SciGPU {

  namespace Legion {

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

    void Legion::Enqueue( Task* const theTask ) {
      /*!
	This method accepts a pointer to a single Task,
	and adds it to the pending list for the Legion.
	Note that only the pointer is copied, so care
	must be taken to ensure that the Task object
	itself is not destroyed until it has been
	processed.
	\param theTask Pointer to the Task object
      */

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

      // Add the task to the list
      this->pending.push_back( theTask );

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

      // Add the tag to the set of seen tags
      boost::mutex::scoped_lock seenLock(this->seenMutex);
      this->seenTags.insert( theTask->tag );
    }

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

    Task* Legion::CollectOne( void ) {

      Task* res;

      // Lock the 'complete' mutex
      boost::mutex::scoped_lock lock(this->completeMutex);

      // Check if the size is zero, and wait if necessary
      while( this->complete.size() == 0 ) {
	this->completeCond.wait( lock );
      }

      // The completeMutex will now be locked

      // Acquire the item
      res = this->complete.front();

      // Pop it off the list
      this->complete.pop_front();

      return( res );
    }
    

    // -------------------------------------------------------
    
    const size_t Legion::CountPending( void ) {
      boost::mutex::scoped_lock lock(this->pendingMutex);

      return( this->pending.size() );
    }

    const size_t Legion::CountComplete( void ) {
      boost::mutex::scoped_lock lock(this->completeMutex);

      return( this->complete.size() );
    }

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

    void Legion::SeenTags( TagSet& seen ) {
      /*!
	This method gives the user a way of examining
	which Task::tag values have been seen by
	the Legion.
	It is destructive for its argument \a seen.
	@param[out] seen On exit, the set of tags seen
      */
      seen.clear();

      boost::mutex::scoped_lock lock(this->seenMutex);
      
      seen = this->seenTags;
    }

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

    void Legion::CompletedStatistics( ManipleStats& cStats ) {
     
      cStats.clear();

      boost::mutex::scoped_lock lock( this->compStatsMutex );

      cStats = this->compStats;
    }

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

    void Legion::PrintCompletedStats( std::ostream& os ) {

      ManipleStats cStats;
      TagSet tags;

      os << "Statistics for Completed Tasks" << endl;

      this->CompletedStatistics( cStats );
      this->SeenTags( tags );

      this->PrintStatistics( os, tags, cStats );
    }

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

    void Legion::PrintStatistics( std::ostream& os,
				  const TagSet& tags,
				  ManipleStats& stats ) {
      /*!
	Pretty prints all of the statistics
	to the designated output stream.
	This is private because it gets a little ugly.
	
	For example, the \a stats argument isn't \c const,
	since we are going to mess around with it.
      */

      const int fieldWidth = 6;

      stringstream hdStream;
      hdStream << "Maniple " << (*stats.begin()).first;
      
      const unsigned int hdSize = hdStream.str().size();
      const string hsep( 1+hdSize+(fieldWidth*(2+tags.size())), '-' );

      TagSet::const_iterator tagIt;

      // Print the list of observed tags
      os << setw(hdSize) << left << "Tags Observed";
      os << right << ":";
      for( tagIt=tags.begin(); tagIt!=tags.end(); tagIt++ ) {
	os << setw(fieldWidth) << *tagIt;
      }
      os << endl;

      os << hsep << endl;

      // Print the stats by maniple
      ManipleStats::iterator mIt;
      TaskCount tagCounts;
      unsigned int taskTot = 0;
      for( mIt=stats.begin(); mIt!=stats.end(); mIt++ ) {

	os << "Maniple " << (*mIt).first << ":";

	unsigned int manipleTot = 0;
	
	for( tagIt=tags.begin(); tagIt!=tags.end(); tagIt++ ) {
	  // Get the value for this Maniple and Tag (will insert zero)
	  unsigned int val = (mIt->second)[ *tagIt ];
	  
	  // Accumulate
	  tagCounts[ *tagIt ] += val;
	  manipleTot += val;

	  // Print
	  os << setw(fieldWidth) << val;
	}

	os << setw(fieldWidth) << "|"
	   << setw(fieldWidth) << manipleTot << endl;
	taskTot += manipleTot;
      }
      
      os << hsep << endl;

      // Print the totals
      os << setw(hdSize) << left << "Totals";
      os << right << ":";
      for( tagIt=tags.begin(); tagIt!=tags.end(); tagIt++ ) {
	os << setw(fieldWidth) << tagCounts[ *tagIt ];
      }
      os << setw(6) << "|" << setw(6) << taskTot << endl;
      
    }

  }
}
