#include "SgEngine.h"
#include <cstring>
#include <cstdio>
#include <cmath>

SgEngine::SgEngine( ProblemDomain *p ) :
    problem( p ),
    population( p->getNumberOfGenes(), p->getNumberOfAlleles() ),
		generationCount(0),
    bestFitness( ProblemDomain::NEGATIVE_INFINITY ),
    bestIndividual( new AlleleType[p->getNumberOfGenes()] ),
    individualOne( new AlleleType[p->getNumberOfGenes()] ),
    individualTwo( new AlleleType[p->getNumberOfGenes()] ),
    sizeOfIndividual( p->getNumberOfGenes() * sizeof( AlleleType ) ) { /* Empty. */ }

SgEngine::~SgEngine() {
  delete [] bestIndividual;
  delete [] individualOne;
  delete [] individualTwo;
}

const AlleleType * const SgEngine::getBestIndividual() const {
  return bestIndividual;
}

const Population * const SgEngine::getPopulation() const {
  return &population;
}

FloatType SgEngine::getBestFitness() const {
  return bestFitness;
}

unsigned SgEngine::getGenerationCount() const {
	return generationCount;
}

void SgEngine::doOneGeneration( bool verbose ) {
  generationCount += 1;
  population.selectIndividual( individualOne );
  population.selectIndividual( individualTwo );
  AlleleType *winner, *loser;
  ProblemDomain::TournamentReturn tournamentResult = problem->tournament( individualOne, individualTwo );
  switch ( tournamentResult.winner ) {
    case ProblemDomain::TournamentReturn::LEFT:
      winner = individualOne;
      loser = individualTwo;
      break;
    case ProblemDomain::TournamentReturn::RIGHT:
      winner = individualTwo;
      loser = individualOne;
    case ProblemDomain::TournamentReturn::TIE:
      /* Do nothing. */
      break;
  }
  if ( tournamentResult.winner != ProblemDomain::TournamentReturn::TIE ) {
    population.rewardGenes( winner );
    population.penalizeGenes( loser );
  }
  if ( tournamentResult.winningFitness > bestFitness ) {
    std::memcpy( bestIndividual, winner, sizeOfIndividual );
    bestFitness = tournamentResult.winningFitness;
    if ( verbose )
      printBestIndividual( true );
  }
}

void SgEngine::printBestIndividual( bool isNew ) const {
  std::printf( "%sest individual has fitnesss %.3f ::\n  ",
               isNew ? "New b" : "B", bestFitness );
  if ( bestFitness != ProblemDomain::NEGATIVE_INFINITY )
    for ( unsigned locus = 0; locus < problem->getNumberOfGenes(); locus++ )
      std::printf( "%*d",
                   static_cast<int>( 1.7 + std::log10( problem->getNumberOfAlleles()[locus] ) ),
                   bestIndividual[locus] );
  std::printf( "\n" );
}
