/*
 * Copyright 2016 by Romaric Pighetti, CNRS, I3S
 * Licensed under the Academic Free License version 3.0
 */
package fr.unice.i3s.keia.irWithIga;

import cec2013.CEC2013;
import cec2013.Func;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.internal.Lists;
import ec.EvolutionState;
import ec.util.Output;
import ec.util.ParameterDatabase;
import fr.unice.i3s.keia.irWithIga.statistics.Cec2013StatisticsMOBiDE;

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

public class Cec2013ExperimentLauncherPop10 {

    @Parameter(names = { "-o" }, description = "output file")
    private static String            outputFile = "2014_12_19_nsga2mm_cec2013_Vincent_3D_maxim_1e-3.csv";

    @Parameter(names = { "-p" }, description = "parameters file")
    private static String            paramsFile = "\\Users\\root\\Dev\\NSGAII\\params\\algorithms\\nsga2_cec2013.params";

    @Parameter(names = { "-f" },
            description = "Functions in the competition to use")
    private static List<Integer>     functions  = Lists.newArrayList();

    private static ArrayList<String> argv       = new ArrayList<String>();

    public static void main(String[] argsv) {
        Cec2013ExperimentLauncherPop10 launcher = new Cec2013ExperimentLauncherPop10();
        new JCommander(launcher, argsv);

        CEC2013 comp = new CEC2013();

        if (functions.isEmpty()) {
            for (int i = 0; i < comp.getFunctions().size(); i++) {
                functions.add(i);
            }
        }

        argv.add("-file");
        argv.add(paramsFile);
        argv.add("-p");
        argv.add("jobs=50");

        for (int i = 0; i < functions.size(); i++) {

            /*
             * Setup the specific parameters for the function under study
             */
            int currentFunction = functions.get(i);

            CEC2013 cec = new CEC2013();
            Func f = cec.getFunctions().get(currentFunction);
            long popsize = f.getDimension() * 100 * 10;
            long numGen = CEC2013.getMaxfes(currentFunction + 1) / popsize;
            List<cec2013.ClosedInterval.Double> bounds = f.getBounds();
            double maxFit = CEC2013.getFitnessGoptima(currentFunction + 1);

            ArrayList<String> arguments = (ArrayList<String>) argv.clone();

            arguments.add("-p");
            arguments.add("stat.child.0.function=" + currentFunction);

            arguments.add("-p");
            arguments.add("eval.problem.objective.0.function=" + currentFunction);

            arguments.add("-p");
            arguments.add("generations=" + numGen);

            arguments.add("-p");
            arguments.add("pop.subpop.0.size=" + popsize);

            arguments.add("-p");
            arguments.add("pop.default-subpop.size=" + popsize);

            arguments.add("-p");
            arguments.add("vector.species.genome-size=" + f.getDimension());

            for (int d = 0; d < bounds.size(); d++) {
                arguments.add("-p");
                arguments.add("pop.subpop.0.species.min-gene." + d + "=" + bounds.get(d).lower);
                arguments.add("-p");
                arguments.add("pop.subpop.0.species.max-gene." + d + "=" + bounds.get(d).upper);
                arguments.add("-p");
                arguments.add("vector.species.min-gene." + d + "=" + bounds.get(d).lower);
                arguments.add("-p");
                arguments.add("vector.species.max-gene." + d + "=" + bounds.get(d).upper);
            }

            arguments.add("-p");
            arguments.add("multi.fitness.max.0=" + maxFit);

            try {
                launch(arguments.toArray(new String[0])).join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Cec2013StatisticsMOBiDE.printToFile(outputFile);

        System.exit(0);
    }

    public static Thread launch(String[] arguments) {
        final String[] threadArgs = arguments;
        Thread t = new Thread() {
            public void run() {
                EvolutionState state;
                ParameterDatabase parameters;
                String[] args = threadArgs;

                // if we're loading from checkpoint, let's finish out the most
                // recent job
                state = ec.Evolve.possiblyRestoreFromCheckpoint(args);
                int currentJob = 0; // the next job number (0 by default)

                // this simple job iterator just uses the 'jobs' parameter,
                // iterating from 0 to 'jobs' - 1
                // inclusive. The current job number is stored in state.jobs[0],
                // so we'll begin there if we had loaded from checkpoint.

                if (state != null) // loaded from checkpoint
                {
                    // extract the next job number from state.job[0] (where in
                    // this
                    // example we'll stash it)
                    try {
                        if (state.runtimeArguments == null)
                            Output.initialError("Checkpoint completed from job started by foreign program (probably GUI).  Exiting...");
                        args = state.runtimeArguments; // restore runtime
                                                       // arguments from
                                                       // checkpoint
                        currentJob = ((Integer) (state.job[0])).intValue() + 1; // extract
                                                                                // next
                                                                                // job
                                                                                // number
                    } catch (Exception e) {
                        Output.initialError("EvolutionState's jobs variable is not set up properly.  Exiting...");
                    }

                    state.run(EvolutionState.C_STARTED_FROM_CHECKPOINT);
                    ec.Evolve.cleanup(state);
                }

                // A this point we've finished out any previously-checkpointed
                // job. If
                // there was
                // one such job, we've updated the current job number
                // (currentJob) to
                // the next number.
                // Otherwise currentJob is 0.

                // Now we're going to load the parameter database to see if
                // there are
                // any more jobs.
                // We could have done this using the previous parameter
                // database, but
                // it's no big deal.
                parameters = ec.Evolve.loadParameterDatabase(args);
                if (currentJob == 0) // no current job number yet
                    currentJob = parameters.getIntWithDefault(
                            new ec.util.Parameter("current-job"), null, 0);
                if (currentJob < 0)
                    Output.initialError("The 'current-job' parameter must be >= 0 (or not exist, which defaults to 0)");

                int numJobs = parameters.getIntWithDefault(
                        new ec.util.Parameter("jobs"), null, 1);
                if (numJobs < 1)
                    Output.initialError("The 'jobs' parameter must be >= 1 (or not exist, which defaults to 1)");

                // Now we know how many jobs remain. Let's loop for that many
                // jobs. Each
                // time we'll
                // load the parameter database scratch (except the first time
                // where we
                // reuse the one we
                // just loaded a second ago). The reason we reload from scratch
                // each
                // time is that the
                // experimenter is free to scribble all over the parameter
                // database and
                // it'd be nice to
                // have everything fresh and clean. It doesn't take long to load
                // the
                // database anyway,
                // it's usually small.
                for (int job = currentJob; job < numJobs; job++) {
                    // load the parameter database (reusing the very first
                    // if it
                    // exists)
                    if (parameters == null)
                        parameters = ec.Evolve.loadParameterDatabase(args);

                    // Initialize the EvolutionState, then set its job
                    // variables
                    state = ec.Evolve.initialize(parameters, job); // pass
                                                                   // in
                                                                   // job#
                                                                   // as
                    // the seed
                    // increment
                    state.output.systemMessage("Job: " + job);
                    state.job = new Object[1]; // make the job argument
                                               // storage
                    state.job[0] = new Integer(job); // stick the current
                                                     // job in our
                                                     // job storage
                    state.runtimeArguments = args; // stick the runtime
                                                   // arguments in
                                                   // our storage
                    if (numJobs > 1) // only if iterating (so we can be
                                     // backwards-compatible),
                    {
                        String jobFilePrefix = "job." + job + ".";
                        state.output.setFilePrefix(jobFilePrefix); // add a
                                                                   // prefix
                                                                   // for
                                                                   // checkpoint/output
                                                                   // files
                        state.checkpointPrefix = jobFilePrefix
                                + state.checkpointPrefix; // also set up
                                                          // checkpoint
                                                          // prefix
                    }

                    // Here you can set up the EvolutionState's parameters
                    // further
                    // before it's setup(...).
                    // This includes replacing the random number generators,
                    // changing values in state.parameters,
                    // changing instance variables (except for job and
                    // runtimeArguments, please), etc.

                    // Add the evaluator to the EvolutionState. The
                    // EvolutionState must
                    // inherit from
                    // LaiEvolutionState.
                    try {
                        Class evoStateClass = state.getClass();
                        // Class laiEvoStateClass = LaiEvolutionState.class;
                        // if (laiEvoStateClass.isAssignableFrom(evoStateClass)) {

                            // LaiEvolutionState laiState = (LaiEvolutionState) state;

                            // alternative to get the class to instanciate
                            // parameters.getClassForParameter(new
                            // ec.util.Parameter("pop.subpop.0.species.ind"),
                            // null,
                            // Class.forName("fr.unice.i3s.keia.irWithIga.perronnin.PerronninIndividual"));

                        //} else {
                        //    throw new Exception(
                        //            "EvolutionState must inherit from LaiEvolutionState");
                        //}
                    } catch (Exception e) {
                        e.printStackTrace();
                        return;
                    }

                    // now we let it go
                    state.run(EvolutionState.C_STARTED_FRESH);
                    ec.Evolve.cleanup(state); // flush and close various
                                              // streams,
                    // print out
                    // parameters if necessary
                    parameters = null; // so we load a fresh database next
                                       // time
                                       // around

                }

            }
        };
        // state.run(EvolutionState.C_STARTED_FRESH);
        t.start();
        return t;
    }
}
