#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <iomanip>
#include <map>
using namespace std;

#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <boost/program_options.hpp>
namespace bpo = boost::program_options;

#include "legion.hpp"
#include "cpumaniple.hpp"
#include "cputask.hpp"

// ==============================================================


class BasicTask : public SciGPU::Legion::CPUtask {
public:
  // Constructor
  BasicTask( void ) : CPUtask(),
		      initial(0),
		      final(0) {} ;

  // Initial and final 'work' values
  int initial, final;

  // Over-ride to perform some work
  virtual bool Perform( void ) {
    final = initial+1;    
    boost::posix_time::milliseconds slp(10);  
    boost::this_thread::sleep( slp );
    return( true );
  }
};


ostream& operator<<( ostream& os, const BasicTask& task ) {

  os << "Task performed by Maniple " << task.GetManipleID()
     << ": " << setw(6) << task.initial
     << "->" << setw(6) << task.final;
  
  return( os );
}


// ==============================================================

const unsigned int nManiplesDefault = 2;
const unsigned int nTasksDefault = 20;

unsigned int nManiples;
unsigned int nTasks;


void ReadCommandLine( int ac, char* av[] ) {

  try {
    bpo::options_description generic("Generic options");
    generic.add_options()
      ("help", "Produce help message" )
      ;

    bpo::options_description control("Control options");
    control.add_options()
      ("maniples",bpo::value<unsigned int>(&nManiples)->default_value(nManiplesDefault),"Number of maniples to enlist")
      ("tasks",bpo::value<unsigned int>(&nTasks)->default_value(nTasksDefault),"Number of tasks to perform")
      ;

    bpo::options_description cmdLine;
    cmdLine.add(generic).add(control);

    bpo::variables_map vm;
    bpo::store( bpo::command_line_parser(ac, av).
		options(cmdLine).run(), vm );
    bpo::notify( vm );
    
    if( vm.count( "help" ) ) {
      cout << cmdLine << endl;
      exit( EXIT_SUCCESS );
    }
  }
  catch( exception& e ) {
    cerr << "Error: " << e.what() << endl;
    exit( EXIT_FAILURE );
  }
  catch( ... ) {
    cerr << "Unknown exception" << endl;
    exit( EXIT_FAILURE );
  }

}

  



// ==============================================================

int main( int argc, char *argv[] ) {

  ReadCommandLine( argc, argv );

  cout << "Legion Basic Work" << endl;
  cout << "=================" << endl << endl;

  // Create the Legion
  SciGPU::Legion::Legion myLegion;
  

  // Give the Legion some Maniples
  for( unsigned int i=0; i<nManiples; i++ ) {
    myLegion.AddManiple( SciGPU::Legion::CPUmaniple() );
  }

  // Declare the list of tasks
  vector<BasicTask> tasks;
  for( unsigned int i=0; i<nTasks; i++ ) {
    BasicTask nxt;
    nxt.initial = std::rand() % 65536;
    tasks.push_back( nxt );
  }

  // Enqueue them
  for( unsigned int i=0; i<tasks.size(); i++ ) {
    myLegion.Enqueue( &(tasks.at(i)) );
  }

  // Wait for completion
  volatile size_t nComplete;
  do {
    boost::posix_time::milliseconds slp(100);  
    boost::this_thread::sleep( slp );
    nComplete = myLegion.CountComplete();
  } while( nComplete < tasks.size() );

  // Print some results
  cout << endl<< "Results:" << endl;
  for( unsigned int i=0; i < tasks.size(); i++ ) {
    cout << tasks.at(i) << endl;
  }

  cout << endl;
  
  // Print some stat
  myLegion.PrintCompletedStats( cout );
  cout << endl;

  return( EXIT_SUCCESS );
}
