/*
 * Encog(tm) Core v3.2 - Java Version
 * http://www.heatonresearch.com/encog/
 * https://github.com/encog/encog-java-core
 
 * Copyright 2008-2013 Heaton Research, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *   
 * For more information on Heaton Research copyrights, licenses 
 * and trademarks visit:
 * http://www.heatonresearch.com/copyright
 */
package org.encog.ml.ea.population;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.encog.ml.BasicML;
import org.encog.ml.ea.genome.Genome;
import org.encog.ml.ea.genome.GenomeFactory;
import org.encog.ml.ea.species.BasicSpecies;
import org.encog.ml.ea.species.Species;

/**
 * Defines the basic functionality for a population of genomes. The population
 * is made up of species. These species contain the individiual genomes that
 * make up the population. If you do not want to use species, then create one
 * species that holds every genome.
 */
public class BasicPopulation extends BasicML implements Population,
		Serializable {

	/**
	 * The serial id.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * The object name.
	 */
	private String name;

	/**
	 * The species that make up the population.
	 */
	private final List<Species> species = new ArrayList<Species>();

	/**
	 * The best genome.
	 */
	private Genome bestGenome;

	/**
	 * A factory that can be used to store create genomes.
	 */
	private GenomeFactory genomeFactory;

	/**
	 * How many genomes should be created.
	 */
	private int populationSize;

	/**
	 * Construct an empty population.
	 */
	public BasicPopulation() {
		this.populationSize = 0;
	}

	/**
	 * Construct a population.
	 * 
	 * @param thePopulationSize
	 *            The population size.
	 */
	public BasicPopulation(final int thePopulationSize,
			final GenomeFactory theGenomeFactory) {
		this.populationSize = thePopulationSize;
		this.genomeFactory = theGenomeFactory;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void clear() {
		this.species.clear();

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Species createSpecies() {
		final Species species = new BasicSpecies();
		species.setPopulation(this);
		getSpecies().add(species);
		return species;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Species determineBestSpecies() {
		for (final Species species : this.species) {
			if (species.getMembers().contains(this.bestGenome)) {
				return species;
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Genome> flatten() {
		final List<Genome> result = new ArrayList<Genome>();
		for (final Species species : this.species) {
			result.addAll(species.getMembers());
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Genome getBestGenome() {
		return this.bestGenome;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public GenomeFactory getGenomeFactory() {
		return this.genomeFactory;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getMaxIndividualSize() {
		return Integer.MAX_VALUE;
	}

	/**
	 * @return The name.
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getPopulationSize() {
		return this.populationSize;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Species> getSpecies() {
		return this.species;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setBestGenome(final Genome genome) {
		this.bestGenome = genome;

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setGenomeFactory(final GenomeFactory factory) {
		this.genomeFactory = factory;
	}

	/**
	 * Set the name.
	 * 
	 * @param theName
	 *            The new name.
	 */
	public void setName(final String theName) {
		this.name = theName;

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setPopulationSize(final int thePopulationSize) {
		this.populationSize = thePopulationSize;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int size() {
		return flatten().size();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void updateProperties() {

	}

	/**
	 * Purge any invalid genomes.
	 */
	public void purgeInvalidGenomes() {
		// remove any invalid genomes
		int speciesNum = 0;
		while (speciesNum < getSpecies().size()) {
			Species species = getSpecies().get(speciesNum);

			int genomeNum = 0;
			while (genomeNum < species.getMembers().size()) {
				Genome genome = species.getMembers().get(genomeNum);
				if (Double.isInfinite(genome.getScore())
						|| Double.isInfinite(genome.getAdjustedScore())
						|| Double.isNaN(genome.getScore())
						|| Double.isNaN(genome.getAdjustedScore())) {
					species.getMembers().remove(genome);
				} else {
					genomeNum++;
				}
			}
			
			// is the species now empty?
			if (species.getMembers().size() == 0) {
				getSpecies().remove(species);
			} else {
				// new leader needed?
				if( !species.getMembers().contains(species.getLeader()) ) {
					species.setLeader(species.getMembers().get(0));
					species.setBestScore(species.getLeader().getScore());
				}
				
				// onto the next one!
				speciesNum++;
			}
		}
	}
}
