// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************

#include "ExperimentCreator.h"

#include "ExpSetup.h"
#include "io/last_gener.h"

namespace aevol {

ExperimentCreator::ExperimentCreator() {
  checkpoint_handler_ = CheckpointHandler::make_default();
}

auto ExperimentCreator::make_from_params(const ParamValues& params) -> std::unique_ptr<ExperimentCreator> {
  auto experiment_creator = std::unique_ptr<ExperimentCreator>(new ExperimentCreator());
  experiment_creator->load_params(params);
  return experiment_creator;
}

void ExperimentCreator::create_random_clonal_population(JumpingMT& prng, dna_size_type chrsm_init_len) {
  auto indiv = Individual::make_random(prng, chrsm_init_len, exp_setup->w_max(), *target_);

  fill_population_with_clones(std::move(indiv));
}

void ExperimentCreator::create_clonal_population_from_sequences(const std::vector<std::string>& sequences) {
  auto indiv = exp_setup->diploid()
                   ? Individual::make_from_sequences(sequences[0], sequences[1])
                   : Individual::make_from_sequence(sequences.front());

  fill_population_with_clones(std::move(indiv));
}

void ExperimentCreator::fill_population_with_clones(std::unique_ptr<Individual> indiv) {
  population_.fill_with_clones(grid_.width() * grid_.height(), std::move(indiv));
}

void ExperimentCreator::load_params(const ParamValues& params) {
  // Initialize master prng (will be used to generate seeds for the other prngs)
  auto prng = std::make_shared<JumpingMT>(params.seed);

  // Initialize mut_prng, stoch_prng, world_prng :
  // if mut_seed (respectively stoch_seed) not given in param.in, choose it at random
  int32_t mut_seed = (params.mut_seed != 0) ? params.mut_seed : prng->random(1000000);
  int32_t stoch_seed = (params.stoch_seed != 0) ? params.stoch_seed : prng->random(1000000);
  auto mut_prng   = std::make_shared<JumpingMT>(mut_seed);
  auto stoch_prng = std::make_shared<JumpingMT>(stoch_seed);
  auto world_prng = std::make_shared<JumpingMT>(prng->random(1000000));

  // 1) ------------------------------------- Initialize the experimental setup
  exp_setup->load_from_params(params);

  // 2) ---------------------------------------------- Create phenotypic target
  target_ = PhenotypicTarget::make_from_gaussians(params.std_env_gaussians, params.fuzzy_flavor, params.env_sampling);

  // 3) --------------------------------------------- Create the new population
  // Set the grid
  grid_.reset_grid_size(params.grid_width, params.grid_height);
  auto grid_size = grid_.width() * grid_.height();
  for (auto pos = decltype(grid_size){0} ; pos < grid_size ; ++pos) {
    grid_[pos].set_mutation_prng(JumpingMT(mut_prng->random(1000000)));
    grid_[pos].set_selection_prng(JumpingMT(prng->random(1000000)));
  }

  // Set output frequencies (checkpoints, trees, stats, ...)
  checkpoint_frequency_ = params.checkpoint_frequency;
  // NOTA: no trees with sexual reproduction
  tree_output_frequency_ = exp_setup->sex() ? std::nullopt : params.tree_output_frequency;
  stats_orchestrator_ = std::make_unique<StatsOrchestrator>(params.best_indiv_stats_output_frequency,
                                                            params.pop_stats_output_frequency);
}

void ExperimentCreator::write_checkpoint() const {
  checkpoint_handler_->write_checkpoint(AeTime::time(),
                                        population_.to_indivs_idxs_sorted_map(),
                                        grid_,
                                        *target_,
                                        *exp_setup,
                                        checkpoint_frequency_,
                                        tree_output_frequency_,
                                        stats_orchestrator_->best_indiv_stats_output_frequency(),
                                        stats_orchestrator_->mean_stats_output_frequency());
  aevol::write_last_gener_file(AeTime::time());
}

}  // namespace aevol
