// ****************************************************************************
//
//          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/>.
//
// ****************************************************************************

#ifndef AEVOL_EXP_SETUP_H_
#define AEVOL_EXP_SETUP_H_

// =================================================================
//                              Includes
// =================================================================
#include <cinttypes>
#include <cstdlib>
#include <set>

#include "biochemistry/GenomeLimits.h"
#include "checkpointing/mxifstream.h"
#include "checkpointing/mxofstream.h"
#include "parameters/ParamValues.h"
#include "SelectionParams.h"
#include "JumpingMT.h"
#include "fuzzy/FuzzyFactory.h"
#include "fuzzytypes.h"
#include "fuzzy/FuzzyFlavor.h"
#include "AlignmentParams.h"
#include "MutationParams.h"
#ifdef BASE_4
  #include "aevol_4b/biochemistry/AA_2_MWH_Value_Mapping.h"
#endif

namespace aevol {

// ===========================================================================
//                          Class declarations
// ===========================================================================
class ExpSetup {
 public:
  // =======================================================================
  //                             Constructors
  // =======================================================================
  ExpSetup();
  ExpSetup(const ExpSetup&)            = delete;
  ExpSetup(ExpSetup&&)                 = default;
  ExpSetup& operator=(const ExpSetup&) = delete;
  ExpSetup& operator=(ExpSetup&&)      = default;

  // =======================================================================
  //                             Destructors
  // =======================================================================
  virtual ~ExpSetup() noexcept = default;

  static std::unique_ptr<ExpSetup> make_from_checkpoint(std::ifstream& is, mxifstream& mxis, grid_xy_type pop_size);

  void load_from_params(const ParamValues& params);

  // =======================================================================
  //                         Accessors: getters
  // =======================================================================
  auto w_max() const { return w_max_; }
  auto normalize_phenotype() const { return normalize_phenotype_; }

  // ----------------------------------------------------- Selection context
  auto& selection() { return *selection_; }
  const auto& selection() const { return *selection_; }
  auto selection_pressure() const { return selection_->selection_pressure(); }

  // ------------------------------------------------------------ Parameter for SIMD
  std::shared_ptr<MutationParams> mut_params() { return mut_params_;}
  const auto& mutation_params() const { return *mut_params_;}
  const auto* alignment_params() const { return alignment_params_.get(); }

  const auto& genome_limits() const { return *genome_limits_; }
  auto min_genome_length() const { return genome_limits_->min_genome_size(); }
  auto max_genome_length() const { return genome_limits_->max_genome_size(); }

  const auto& fuzzy_factory() const { return *fuzzy_factory_; }

  #ifdef BASE_4
  const auto& aa_2_mwh_value_mapping() const { return *aa_2_mwh_value_mapping_; }
  #endif
  auto farthest() const {return farthest_; }

  auto linear_chrsm() const { return linear_chrsm_; }
  auto diploid() const { return diploid_; }
  auto sex() const { return sex_; }

  // =======================================================================
  //                         Accessors: setters
  // =======================================================================
  void set_w_max(double w_max) { w_max_ = w_max; }

  void set_genome_limits(dna_size_type min_genome_length, dna_size_type max_genome_length) {
    genome_limits_ = std::make_unique<GenomeLimits>(min_genome_length, max_genome_length);
  }

    void set_mutation_parameters(std::shared_ptr<MutationParams> mut_params) { mut_params_ = mut_params; }
    void set_alignment_parameters(std::unique_ptr<align::AlignmentParams> align_params) {
      alignment_params_ = std::move(align_params);
    }

  #ifdef BASE_4
  void reset_aa_2_mwh_value_mapping(std::unique_ptr<aevol_4b::AA_2_MWH_Value_Mapping>&& new_mapping) {
    aa_2_mwh_value_mapping_ = std::move(new_mapping);
  }
  #endif

  // =======================================================================
  //                            Public Methods
  // =======================================================================
  void write_to_checkpoint(std::ofstream& os, mxofstream& mxos) const;
  void load_from_checkpoint(std::ifstream& is, mxifstream& mxis, grid_xy_type pop_size);

  // =======================================================================
  //                           Public Attributes
  // =======================================================================

 protected :
  // =======================================================================
  //                           Protected Methods
  // =======================================================================
  virtual void display() {};

  // =======================================================================
  //                          Protected Attributes
  // =======================================================================
  double w_max_;
  bool normalize_phenotype_;

  // ----------------------------------------------------- Selection context
  std::unique_ptr<SelectionParams> selection_;

  // Mutation rates etc...
  std::shared_ptr<MutationParams> mut_params_;

  // Parameters for sequence alignements
  std::unique_ptr<align::AlignmentParams> alignment_params_ = nullptr;

  std::unique_ptr<GenomeLimits> genome_limits_;

  std::unique_ptr<FuzzyFactory> fuzzy_factory_;

  #ifdef BASE_4
  std::unique_ptr<aevol_4b::AA_2_MWH_Value_Mapping> aa_2_mwh_value_mapping_ =
      std::make_unique<aevol_4b::AA_2_MWH_Value_Mapping>();
  #endif

  /// Forbid that a gene be transcribed onto multiple RNAs,
  /// keep only that RNA whose promoter is the farthest from the gene (i.e. the longest RNA)
  #ifdef BASE_2
  bool farthest_ = false;
  #else // BASE_4
  bool farthest_ = true;
  #endif

  bool linear_chrsm_ = false;
  bool diploid_      = false;
  bool sex_          = false;
};

std::ostream& operator<<(std::ostream&, const ExpSetup&);

// Static instance
extern std::unique_ptr<ExpSetup> exp_setup;

} // namespace aevol

#endif // AEVOL_EXP_SETUP_H_
