/*
 * Copyright 2016 by Romaric Pighetti, CNRS, I3S
 * Licensed under the Academic Free License version 3.0
 *
 * derivative from original work Copyright 2006 by Sean Luke and George Mason University
 */
package fr.unice.i3s.keia.irWithIga.problem;

import ec.*;
import ec.simple.*;
import ec.util.ParamClassLoadException;
import ec.util.Parameter;
import ec.vector.*;
import fr.unice.i3s.keia.irWithIga.objective.Objective;
import fr.unice.i3s.keia.irWithIga.fitness.MObiDE_Fitness;

public class MObiDE_Problem extends Problem implements SimpleProblemForm {

    public static double        fOpt;
    public static long          nfe;

    private static final String P_OBJECTIVE     = "objective";
    private static final String P_NUM_OBJECTIVE = "multi.fitness.num-objectives";

    private Objective[]         objectives      = new Objective[0];

    // nothing....
    public void setup(final EvolutionState state, final Parameter base) {
        super.setup(state, base);

        // Setting objective functions
        int numObjectives = state.parameters.getInt(new Parameter(
                P_NUM_OBJECTIVE), null);
        objectives = new Objective[numObjectives];
        for (int i = 0; i < numObjectives - 1; i++) {
            Parameter param = base;
            param = param.push(P_OBJECTIVE);
            param = param.push(i + "");
            try {
                Objective obj = (Objective) (state.parameters
                        .getClassForParameter(param, null, Objective.class))
                        .newInstance();
                obj.setup(state, param);
                objectives[i] = obj;
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ParamClassLoadException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void prepareToEvaluate(final EvolutionState state,
            final int threadnum) {

        super.prepareToEvaluate(state, threadnum);

        // we will set all indvs evaluation state to not_evaluated
        for (int i = 0; i < state.population.subpops.length; i++) {
            Subpopulation mypop = state.population.subpops[i];
            int t;

            for (t = 0; t < mypop.individuals.length; t++) {
                // TODO: Try to be sure about reevaluation strategy
                // mypop.individuals[t].evaluated=false;

                // sets a circular reference ind<->fit
                ((MObiDE_Fitness) mypop.individuals[t].fitness).individual = mypop.individuals[t];
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see ec.Problem#finishEvaluating(ec.EvolutionState, int)
     */
    @Override
    public void finishEvaluating(EvolutionState state, int threadnum) {
        // TODO Auto-generated method stub
        super.finishEvaluating(state, threadnum);
        // filterArchive();
    }

    public void evaluate(final EvolutionState state, final Individual ind,
            final int subpopulation, final int threadnum) {
        if (!(ind instanceof DoubleVectorIndividual)) {
            state.output
                    .fatal("The individuals for this problem should be DoubleVectorIndividuals.");
        }

        double[] objectives = ((MObiDE_Fitness) ind.fitness).getObjectives();
        if (ind.evaluated == false) {
            for (int i = 0; i < objectives.length - 1; i++) {
                objectives[i] = this.objectives[i].evaluate(state, ind,
                        subpopulation, threadnum);
            }
            ind.evaluated = true;
        }

        // paper eq.(9b)
        objectives[objectives.length - 1] = getDistAll(state, ind,
                subpopulation);

        // No new array created, objectives are updated in the fitness as no new
        // reference is created. No need to call setObjectives().
    }

    private double getDistAll(final EvolutionState state, final Individual ind,
            int subpopulation) {
        Subpopulation mypop = state.population.subpops[subpopulation];
        double retval = 0;
        for (int t1 = 0; t1 < mypop.individuals.length; t1++) {
            retval += distance_Euclide_Individuals(ind, mypop.individuals[t1]);
        }

        // paper eq.(9b)
        return retval / mypop.individuals.length;
    }

    /**
     * Euclidean distance between individuals
     *
     * @param ind1
     * @param ind2
     * @return
     */
    private static double distance_Euclide_Individuals(Individual ind1,
            Individual ind2) {
        int tg;
        DoubleVectorIndividual iv1 = (DoubleVectorIndividual) ind1;
        DoubleVectorIndividual iv2 = (DoubleVectorIndividual) ind2;
        double[] g1 = iv1.genome;
        double[] g2 = iv2.genome;
        double tmp;
        int numDecisionVars = g1.length;
        double sum = 0;

        for (tg = 0; tg < numDecisionVars; tg++) {
            tmp = g1[tg] - g2[tg];
            sum += tmp * tmp;
        }
        return Math.sqrt(sum);
    }

}
