package AI::NaturalSelection::Threaded;

use strict;
use threads;

sub spawn {
  my ($self)	= @_;

  # do we end the tournament just yet?
  while (!&{$self->{EVALUATE}}(++$self->{GENERATION}, \@{$self->{DNA}})) {

    foreach my $chromosome (0 .. $self->{CHROMOSOMES}) {
      $chromosome == $self->{CHROMOSOMES} ?
        $self->{POPULATION}[$chromosome] = threads->create(sub { }) :
        $self->{POPULATION}[$chromosome] =
          threads->create($self->{ENVIRONMENT}, $self, $self->{DNA}[$chromosome], $chromosome);
    }

    # wait for threads to exit and get return values
    foreach my $thread (0 .. $self->{CHROMOSOMES} - 1) {
      ($self->{DNA}[$thread]) = $self->{POPULATION}[$thread]->join();
    }

    my @surviving;
    if (ref($self->{SURVIVAL})) {
      @surviving = &{$self->{SURVIVAL}}(@$self->{DNA}[$_]);
    }
    else {
      my @fitnesses = map { &{$self->{FITNESS}}($self->{DNA}[$_]) } 0 .. $self->{CHROMOSOMES} - 1;
      @surviving = (
	sort {
		$self->{FITGREAT} ?
		$fitnesses[$b] <=> $fitnesses[$a] :
		$fitnesses[$a] <=> $fitnesses[$b]
	} 0 .. $self->{CHROMOSOMES} - 1
      )[0 .. $self->{SURVIVAL} - 1];
    }

    foreach my $chromosome (0 .. $self->{CHROMOSOMES} - 1) {
      next if grep { $_ == $chromosome } @surviving;
  
      if (@surviving == 1) {
        $self->{DNA}[$chromosome] = &{$self->{CROSSOVER}}(
          map { $self->{DNA}[$surviving[0]] } 1, 2
        );
      }
      else {
        my ($mother, $father);
        do {
          $mother = int(rand(@surviving));
          $father = int(rand(@surviving));
        } while ($mother != $father);
  
        $self->{DNA}[$chromosome] =
          &{$self->{CROSSOVER}}($self->{DNA}[$surviving[$mother]], $self->{DNA}[$surviving[$father]]);
      }   
      $self->{DNA}[$chromosome] = &{$self->{MUTATE}}($self, $self->{DNA}[$chromosome], $chromosome)
        if (rand(1) < $self->{MUTATERATE});
    }
  }  
}

1;
