package cecj.interaction;

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

import cecj.problems.TestBasedProblem;

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

/**
 * 
 * @author Marcin Szubert
 * 
 */
public class LearnerTeacherInteractionScheme implements InteractionScheme {

	private static final String P_POP = "pop";
	private static final String P_ROLE = "role";
	private static final String P_SIZE = "subpops";
	private static final String P_SUBPOP = "subpop";

	/**
	 * Number of subpopulations.
	 */
	private int numSubpopulations;

	private TestBasedProblem problem;

	public enum Role {
		LEARNER, TEACHER
	}

	private Role[] subpopulationRoles;

	public void setup(EvolutionState state, Parameter base) {
		if (!(state.evaluator.p_problem instanceof TestBasedProblem)) {
			state.output.fatal("Learner-teacher interactions need asymmetric problem definition\n");
		} else {
			problem = (TestBasedProblem) state.evaluator.p_problem;
		}

		Parameter popSizeParameter = new Parameter(P_POP).push(P_SIZE);
		numSubpopulations = state.parameters.getInt(popSizeParameter, null, 0);
		if (numSubpopulations <= 0) {
			state.output.fatal("Population size must be > 0.\n", popSizeParameter);
		}
		
		subpopulationRoles = new Role[numSubpopulations];
		for (int subpop = 0; subpop < numSubpopulations; subpop++) {
			Parameter subpopRoleParam = base.push(P_SUBPOP).push("" + subpop).push(P_ROLE);
			String role = state.parameters.getString(subpopRoleParam, null);
			if (role == null) {
				state.output.fatal("Subpopulation role must be specified for the learner-teacher "
						+ "interactions scheme\n", subpopRoleParam);
			} else {
				try {
					subpopulationRoles[subpop] = Enum.valueOf(Role.class, role.toUpperCase());
				} catch (IllegalArgumentException ex) {
					state.output.fatal("Subpopulation role " + role
							+ " does not exist in the learner-teacher interactions scheme");
				}
			}
		}
	}

	public List<List<InteractionResult>> performInteractions(EvolutionState state, int subpop,
			List<List<Individual>> opponents) {

		List<List<InteractionResult>> subpopulationResults = new ArrayList<List<InteractionResult>>();
		Individual[] inds = state.population.subpops[subpop].individuals;

		for (Individual ind : inds) {
			List<InteractionResult> results = new ArrayList<InteractionResult>();
			for (int subpop2 = 0; subpop2 < numSubpopulations; subpop2++) {
				if (subpopulationRoles[subpop2] != subpopulationRoles[subpop]) {
					List<Individual> curOpponents = opponents.get(subpop2);
					for (Individual opponent : curOpponents) {
						if (subpopulationRoles[subpop] == Role.LEARNER) {
							results.add(problem.test(state, ind, opponent).first);
						} else {
							results.add(problem.test(state, opponent, ind).second);
						}
					}
				}
			}
			subpopulationResults.add(results);
		}

		return subpopulationResults;
	}

	public List<Integer> getSubpopulationIndices(Role role) {
		List<Integer> result = new ArrayList<Integer>();
		for (int subpop = 0; subpop < numSubpopulations; subpop++) {
			if (subpopulationRoles[subpop] == role) {
				result.add(subpop);
			}
		}
		return result;
	}
}
