package fr.unice.i3s.keia.irWithIga.statistics;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import cec2013.CEC2013;
import ec.EvolutionState;
import ec.Individual;
import ec.Statistics;
import ec.steadystate.SteadyStateStatisticsForm;
import ec.util.Parameter;
import ec.vector.DoubleVectorIndividual;
import fr.unice.i3s.keia.irWithIga.breeder.MOBiDE_Breeder;
import fr.unice.i3s.keia.irWithIga.evaluator.MOBiDE_Evaluator;

public class Cec2013StatisticsMOBiDE extends Statistics implements
        SteadyStateStatisticsForm {

    private static final long                                        serialVersionUID = -7435692883156598167L;

    // <Function number, <run number, <gen number, numOptima>>>
    private static Map<Integer, Map<Integer, Map<Integer, Integer>>> allResults;

    private int                                                      functionIndex    = -1;
    private int                                                      runIndex         = -1;
    private double                                                   accuracy         = 0;
    private Map<Integer, Integer>                                    currentRun       = new HashMap<Integer, Integer>();

    public void setup(final EvolutionState state, final Parameter base) {
        super.setup(state, base);
        if (allResults == null) {
            allResults = new HashMap<Integer, Map<Integer,Map<Integer,Integer>>>();
        }
        functionIndex = state.parameters.getInt(base.push("function"),
                new Parameter("eval.problem.function"));
        runIndex = (Integer) state.job[0];
        accuracy = state.parameters.getDouble(new Parameter(
                "eval.problem.accuracy"), null);
        if (allResults.get(functionIndex) == null) {
            allResults.put(functionIndex,
                    new HashMap<Integer, Map<Integer, Integer>>());
        }
        allResults.get(functionIndex).put(runIndex, currentRun);
    }
    
    public void preInitializationStatistics(final EvolutionState state) {
//        try {
//            File outputFile = new File("MOBiDE_params.out");
//            PrintWriter statisticslog = new PrintWriter(new BufferedWriter(
//                    new FileWriter(outputFile, true)));
//            state.parameters.list(statisticslog);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    public void postEvaluationStatistics(final EvolutionState state) {
        ArrayList<ArrayList<Double>> population = individualsToArrayList(state.population.subpops[0].individuals);
        MOBiDE_Evaluator evaluator = (MOBiDE_Evaluator) state.evaluator;
        population.addAll(individualsToArrayList(evaluator.getArchive().getIndividuals(0)));
        ArrayList<ArrayList<Double>> seeds = new ArrayList<ArrayList<Double>>();
        // Use functionIndex + 1 because functions expect a number between 1 and 20 and
        // not the index in the function array (from 0 to 19).
        int numberOfOptimaFound = CEC2013.howManyGlobalOptimaInPopulation(
                population, seeds, functionIndex + 1, accuracy,
                CEC2013.getRho(functionIndex + 1));
        currentRun.put(state.generation, numberOfOptimaFound);
//        state.output.message(evaluator.getArchive().subpops[0].individuals.length + "");
//        state.output.message(state.population.subpops[0].individuals.length + "");
    }

    private ArrayList<ArrayList<Double>> individualsToArrayList(
            Individual[] individuals) {
        ArrayList<ArrayList<Double>> result = new ArrayList<ArrayList<Double>>();
        for (int i = 0; i < individuals.length; i++) {
            ArrayList<Double> currentIndividual = new ArrayList<Double>();
            DoubleVectorIndividual individual = (DoubleVectorIndividual) individuals[i];
            for (int j = 0; j < individual.genome.length; j++) {
                currentIndividual.add(individual.genome[j]);
            }
            result.add(currentIndividual);
        }
        return result;
    }

    public static void printToFile(String fileName) {
        if (allResults != null) {
            try {
                File outputFile = new File(fileName);
                PrintWriter statisticslog = new PrintWriter(new BufferedWriter(
                        new FileWriter(outputFile, true)));
                for (Integer function : allResults.keySet()) {
                    statisticslog.println("# Function index" + function);
                    statisticslog.println("# "
                            + new CEC2013().getFunctions().get(function)
                                    .getClass());
                    Map<Integer, Map<Integer, Integer>> currentFunctionResults = allResults.get(function);
                    for (int i = 0; i < currentFunctionResults.size(); i++) {
                        Map<Integer, Integer> currentRun = currentFunctionResults.get(i);
                        statisticslog.println("Run " + i);
                        for (int j = 0; j < currentRun.size(); j++) {
                            statisticslog.print(currentRun.get(j) + ", ");
                        }
                        statisticslog.println();
                    }
                }
                statisticslog.flush();
                statisticslog.close();
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }
}
