package cecj.eval;

import java.util.ArrayList;
import java.util.List;

import cecj.archive.ArchivingSubpopulation;
import cecj.archive.CoevolutionaryArchive;
import cecj.interaction.InteractionResult;
import cecj.sampling.SamplingMethod;

import ec.EvolutionState;
import ec.Individual;
import ec.util.Parameter;

public class ArchivingCoevolutionaryEvaluator extends SimpleCoevolutionaryEvaluator {

	private static final String P_ARCHIVE = "archive";
	private static final String P_ARCHIVE_INDS_WEIGHT = "archive-inds-weight";
	private static final String P_ARCHIVE_SAMPLING_METHOD = "archive-sampling-method";

	private CoevolutionaryArchive archive;

	private List<List<Individual>> archiveOpponents;

	private SamplingMethod[] archiveSamplingMethod;

	private int archiveIndsWeight;

	@Override
	public void setup(final EvolutionState state, final Parameter base) {
		super.setup(state, base);

		Parameter archiveParam = base.push(P_ARCHIVE);
		archive = (CoevolutionaryArchive) (state.parameters
			.getInstanceForParameter(archiveParam, null, CoevolutionaryArchive.class));
		archive.setup(state, base.push(P_ARCHIVE));

		Parameter archiveIndsWeightParam = base.push(P_ARCHIVE_INDS_WEIGHT);
		archiveIndsWeight = state.parameters.getIntWithDefault(archiveIndsWeightParam, null, 1);

		archiveOpponents = new ArrayList<List<Individual>>(numSubpopulations);
		archiveSamplingMethod = new SamplingMethod[numSubpopulations];

		for (int subpop = 0; subpop < numSubpopulations; subpop++) {
			archiveOpponents.add(new ArrayList<Individual>());
			setupArchivingSubpopulation(state, base, subpop);
		}
	}

	private void setupArchivingSubpopulation(EvolutionState state, Parameter base, int subpop) {
		Parameter samplingMethodParam = base.push(P_SUBPOP).push("" + subpop)
			.push(P_ARCHIVE_SAMPLING_METHOD);
		archiveSamplingMethod[subpop] = (SamplingMethod) (state.parameters
			.getInstanceForParameter(samplingMethodParam, null, SamplingMethod.class));
		archiveSamplingMethod[subpop].setup(state, samplingMethodParam);
	}

	@Override
	public void evaluatePopulation(EvolutionState state) {
		if (!(state.population.subpops[0] instanceof ArchivingSubpopulation)) {
			state.output.fatal("Archiving evaluator requires archiving subpopulation");
		}

		super.evaluatePopulation(state);

		for (int subpop = 0; subpop < numSubpopulations; subpop++) {
			archiveOpponents.set(subpop, findOpponentsFromArchive(state, subpop));
		}

		for (int subpop = 0; subpop < numSubpopulations; subpop++) {
			List<List<InteractionResult>> subpopulationResults = interactionScheme
				.performInteractions(state, subpop, archiveOpponents);

			fitnessAggregateMethod[subpop].addToAggregate(state, subpop, subpopulationResults,
															archiveIndsWeight);
			fitnessAggregateMethod[subpop].assignFitness(state, subpop);
			
			if (statistics != null) {
				statistics.printInteractionResults(state, subpopulationResults, subpop);
			}
		}

		archive.submit(state);
	}

	private List<Individual> findOpponentsFromArchive(EvolutionState state, int subpop) {
		List<Individual> archive = ((ArchivingSubpopulation) state.population.subpops[subpop])
			.getArchivalIndividuals();
		return archiveSamplingMethod[subpop].sample(state, archive);
	}
}
