#include "StoppingConditions.h"
#include "Logger.h"
#include "GlobalStatistics.h"
#include "Statistics.h"
#include "Parameters.h"

///////////////////////////////////
// StoppingConditions main class //
///////////////////////////////////

void StoppingConditions::setPollingMethod( ConditionContext context, PollingMethod value ) {
  switch ( context ) {
    case LOCAL:
      localPollingMethod = value;
      break;
    case GLOBAL:
      globalPollingMethod = value;
      break;
  }
}

StoppingConditions::PollingMethod StoppingConditions::getPollingMethod( ConditionContext context ) const {
  switch ( context ) {
    case LOCAL:
      return localPollingMethod;
    case GLOBAL:
      return globalPollingMethod;
    default:
      throw;
  }
}

void StoppingConditions::addLocal( LocalCondition *condition ) {
  localConditions.push_back( condition );
}

void StoppingConditions::addGlobal( GlobalCondition *condition ) {
  globalConditions.push_back( condition );
}

void StoppingConditions::reset( ConditionContext context ) {
  switch ( context ) {
    case LOCAL:
      localConditions.clear();
      break;
    case GLOBAL:
      globalConditions.clear();
      break;
  }
}

void StoppingConditions::reset() {
  reset( LOCAL );
  reset( GLOBAL );
}

bool StoppingConditions::poll( ConditionContext context ) const {
  PollingMethod method = getPollingMethod( context );
  bool result;
  switch ( method ) {
    case DISJUNCTIVE:
      result = false;
      break;
    case CONJUNCTIVE:
      result = true;
      break;
    default:
      throw;
  }
  boost::ptr_vector<Condition>::const_iterator it, last;
  switch ( context ) {
    case LOCAL:
      it = localConditions.begin();
      last = localConditions.end();
      break;
    case GLOBAL:
      it = globalConditions.begin();
      last = globalConditions.end();
      break;
  }
  for ( ; it != last; it++ ) {
    switch ( method ) {
      case DISJUNCTIVE:
        result = result || it->poll();
        break;
      case CONJUNCTIVE:
        result = result && it->poll();
        break;
    }
  }
  return result;
}

bool StoppingConditions::checkEpoch() const {
  return (( Statistics::getInstance()->getNumConvergedAlleles() >=
            Parameters::getInstance()->getEpochY() ) ||
          ( Statistics::getInstance()->getNumGenerationsEpoch() >=
            Parameters::getInstance()->getEpochGenerationLimit() ) );
}


/////////////////////////////////////
// LocalCondition derivate classes //
/////////////////////////////////////
#define L_FMT_T      "Local stopping condition reached\t%s\t%u >= %u"
#define L_FMT_F      "Local stopping condition failed\t%s\t%u < %u"
#define L_CND_CONV   "converged alleles"
#define L_CND_GENER  "generations"
#define L_CND_CPU    "CPU time"
#define L_CND_EPOCH  "epochs"

bool StoppingConditions::LocalConditionConvergedAlleles::poll() const {
  unsigned int lhs = Statistics::getInstance()->getNumConvergedAlleles();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, L_FMT_T, L_CND_CONV, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, L_FMT_F, L_CND_CONV, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::LocalConditionGenerations::poll() const {
  unsigned int lhs = Statistics::getInstance()->getNumGenerationsTotal();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, L_FMT_T, L_CND_GENER, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, L_FMT_F, L_CND_GENER, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::LocalConditionCpuTime::poll() const {
  unsigned int lhs = Statistics::getInstance()->getCpuTime();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, L_FMT_T, L_CND_CPU, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, L_FMT_F, L_CND_CPU, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::LocalConditionEpochs::poll() const {
  unsigned int lhs = Statistics::getInstance()->getNumEpochs();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, L_FMT_T, L_CND_EPOCH, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, L_FMT_F, L_CND_EPOCH, lhs, parameter );
    return false;
  }
}


//////////////////////////////////////
// GlobalCondition derivate classes //
//////////////////////////////////////
#define G_FMT_T      "Global stopping condition reached\t%s\t%u >= %u"
#define G_FMT_F      "Global stopping condition failed\t%s\t%u < %u"
#define G_CND_DEME   "stopped demes"
#define G_CND_CONV   "total converged alleles"
#define G_CND_GENER  "total generations"
#define G_CND_CPU    "CPU time"
#define G_CND_EPOCH  "total epochs"

bool StoppingConditions::GlobalConditionStoppedDemes::poll() const {
  unsigned int lhs = GlobalStatistics::getInstance()->getNumStoppedDemes();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, G_FMT_T, G_CND_DEME, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, G_FMT_F, G_CND_DEME, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::GlobalConditionConvergedAlleles::poll() const {
  unsigned int lhs = GlobalStatistics::getInstance()->getTotalNumConvergedAlleles();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, G_FMT_T, G_CND_CONV, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, G_FMT_F, G_CND_CONV, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::GlobalConditionGenerations::poll() const {
  unsigned int lhs = GlobalStatistics::getInstance()->getTotalNumGenerations();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, G_FMT_T, G_CND_GENER, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, G_FMT_F, G_CND_GENER, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::GlobalConditionCpuTime::poll() const {
  unsigned int lhs = Statistics::getInstance()->getCpuTime();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, G_FMT_T, G_CND_CPU, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, G_FMT_F, G_CND_CPU, lhs, parameter );
    return false;
  }
}

bool StoppingConditions::GlobalConditionEpochs::poll() const {
  unsigned int lhs = GlobalStatistics::getInstance()->getTotalNumEpochs();
  if ( lhs >= parameter ) {
    LOG( Logger::LOGLEVEL_MEDIUM, G_FMT_T, G_CND_EPOCH, lhs, parameter );
    return true;
  } else {
    LOG( Logger::LOGLEVEL_DEBUG, G_FMT_F, G_CND_EPOCH, lhs, parameter );
    return false;
  }
}
