#include "globals.h"
#include "Communication.h"
#include "Parameters.h"
#include "SgEngine.h"
#include "StoppingConditions.h"
#include "VertexCoverProblem.h"

#include <cstdio>
#include <string>
#include <boost/program_options.hpp>

int main( int argc, char *argv[] ) {
  // Initialize MPI
  int rank, p;
  Communication::programStart( &argc, &argv, &rank, &p );

  // Set MPI parameters
  Parameters *params = Parameters::getInstance();
  params->setP( p );
  params->setRank( rank );

  // Load the program options
  boost::program_options::variables_map vm;
  if ( !global::fetchProgramOptions( &argc, &argv, &vm ) ) {
    LOG( Logger::LOGLEVEL_LOW, "Fatal error\tRequired program options missing!" );
    // Deinit MPI and exit
    Communication::programTerminate();
    return 1;
  }

  // Start the logger
  if ( vm.count( "uid" ) )
    Logger::getInstance()->generateFilenames( vm["uid"].as<std::string>().c_str() );
  LOG( Logger::LOGLEVEL_MEDIUM, "Program start" );

  // Set configuration parameters
  params->setEpsilon(
    vm["epsilon"].as<FloatType>() );
  params->setConvergenceThreshold(
    vm["convergenceThreshold"].as<FloatType>() );
  params->setDelta(
    vm["delta"].as<int>() );
  params->setMu(
    vm["mu"].as<FloatType>() );
  params->setGamma(
    vm["gamma"].as<unsigned int>() );
  params->setEpochX(
    vm["epochX"].as<unsigned int>() );
  params->setEpochY(
    vm["epochY"].as<unsigned int>() );
  params->setEpochGenerationLimit(
    vm["epochGenerationLimit"].as<unsigned int>() );
  params->setIntervalUpdateParameters(
    vm["intervalUpdateParameters"].as<unsigned int>() );
  params->setIntervalCheckEpoch(
    vm["intervalCheckEpoch"].as<unsigned int>() );
  params->setIntervalStoppingLocal(
    vm["intervalStoppingLocal"].as<unsigned int>() );
  params->setIntervalStoppingGlobal(
    vm["intervalStoppingGlobal"].as<unsigned int>() );

  // Load problem
  VertexCoverProblem vcp( vm["problemFile"].as<std::string>() );
  if ( rank == 0 )
    vcp.print( true );
  if ( !vcp.isGood() ) {
    LOG( Logger::LOGLEVEL_LOW, "Fatal error\tInvalid problem file <%s>!", vm["problemFile"].as<std::string>().c_str() );
    // Deinit MPI and exit
    Communication::programTerminate();
    return 2;
  }

  // Set problem domain parameters
  params->setNA( vcp.getNumberOfGenes(), vcp.getNumberOfAlleles() );

  // Load stopping conditions
  StoppingConditions stopCon; // Default constructor will set disjunctive polling for both contexts

// Load local stopcons
  stopCon.addLocal( new StoppingConditions::LocalConditionGenerations( 1000*1000 ) );
  stopCon.addLocal( new StoppingConditions::LocalConditionConvergedAlleles(
                      vcp.getNumberOfGenes() - ( vcp.getNumberOfGenes() >> 3 ) ) ); // 87.5%

// Load global stopcons
  if ( rank == 0 ) {
    stopCon.addGlobal( new StoppingConditions::GlobalConditionStoppedDemes(
                         p - ( p >> 4 ) ) ); // 93.75%
    stopCon.addGlobal( new StoppingConditions::GlobalConditionCpuTime( 10 ) );
  }

  // Solve the problem!
  SgEngine engine( &vcp );
  global::solve( &engine, stopCon );

  //Write a report
  if ( rank == 0 ) {
    engine.printBestIndividual( true, false, Logger::LOGLEVEL_MEDIUM, "Program finished\t" );
    Logger::getInstance()->writeReport( engine.getBestFitness(), true );
  }

  // Deinit MPI and exit
  Communication::programTerminate();
  return 0;
}
