text: /**
   *  @note This file is part of MABE, https://github.com/mercere99/MABE2
   *  @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md
   *  @date 2019-2020.
   *
   *  @file  DynamicOrg.hpp
   *  @brief Dynamic organism that can take any number of genomes and brains.
   *  @note Status: ALPHA
   */
  
  #ifndef MABE_DYNAMIC_ORG_H
  #define MABE_DYNAMIC_ORG_H
  
  #include "../core/MABE.hpp"
  #include "../core/Organism.hpp"
  #include "../core/OrganismManager.hpp"
  #include "../core/Genome.hpp"
  #include "../core/Brain.hpp"
  
  #include "../brains/AagosBrain.hpp"
  
  #include "emp/base/vector.hpp"
  #include "emp/math/Distribution.hpp"
  #include "emp/math/random_utils.hpp"
  #include "emp/tools/string_utils.hpp"
  
  namespace mabe {
  
    class DynamicOrg : public OrganismTemplate<DynamicOrg>{
    protected:
      emp::vector<emp::Ptr<Genome>> genomes;
      emp::vector<emp::Ptr<Brain>> brains;
      emp::vector<double> mut_probs;
      emp::DataMap map;
    public:
      DynamicOrg(OrganismManager<DynamicOrg> & _manager)
        : OrganismTemplate<DynamicOrg>(_manager), genomes(0), brains(0) { }
      DynamicOrg(const DynamicOrg &) = default;
      DynamicOrg(DynamicOrg &&) = default;
      DynamicOrg(const emp::vector<emp::Ptr<Genome>> genomes_in, const emp::vector<emp::Ptr<Brain>> brains_in, 
      OrganismManager<DynamicOrg> & _manager) : OrganismTemplate<DynamicOrg>(_manager), genomes(genomes_in), brains(brains_in) { }
      DynamicOrg(size_t genomes_size, size_t brains_size, OrganismManager<DynamicOrg> & _manager)
        : OrganismTemplate<DynamicOrg>(_manager), genomes(genomes_size), brains(brains_size) { }
      ~DynamicOrg() { 
        for (size_t i = 0; i < genomes.size(); i++) {
          genomes[i].Delete();
        }
        for (size_t i = 0; i < brains.size(); i++) {
          brains[i].Delete();
        }
      }
  
      struct ManagerData : public Organism::ManagerData {
        std::string genomes = "bit_genome,int_genome";  ///< string variable holds comma separated genome types
        std::string mut_probs = "0.003,0.003";          ///< string variable holds comma separated mutation probabilities for each genome
        std::string brains = "aagos_brain";             ///< string variable holds comma separated brain types
        std::string output_name = "bits";               ///< Name of trait that should be used to access bits.
        bool init_random = true;                        ///< Should we randomize ancestor?  (false = all zeros)
      };
  
      // concatenates the ToString() of each genome separated by a new-line character and returns it
      // example of aagos ToString() would look like:
      // 1001011010101001101110010010100101
      // 16 4 0 9 12 5 8 13
      std::string ToString() const override {
        std::string out_string = "";
        for (size_t i = 0; i < genomes.size(); i++) {
          out_string += genomes[i]->ToString();
          out_string += "\n";
        }
        return out_string;
      }
  
      // calls mutate on each genome and adds the number of mutations returned from each call
      // returns the total number of mutations across all genomes in vector
      size_t Mutate(emp::Random & random) override {
        size_t num_mut = 0;
        for (size_t i = 0; i < genomes.size(); i++) {
          num_mut += genomes[i]->Mutate(random, mut_probs[i]);
        }
        return num_mut;
      }
  
      void Randomize(emp::Random & random) override {
        for (size_t i = 0; i < genomes.size(); i++) {
          genomes[i]->Randomize(random);
        }
      }
  
      void Initialize(emp::Random & random) override {
        initGenomes(random);
        initMutProbs(random);
        initBrains(random);
        if (SharedData().init_random) Randomize(random);
      }
  
      void initGenomes(emp::Random & random) {
        emp::vector<std::string> split_genomes;
        emp::slice(SharedData().genomes, split_genomes, ',');
        for(size_t i = 0; i < split_genomes.size(); i++) {
          if (split_genomes[i].compare("bit_genome") == 0) {
            emp::Ptr<TypedGenome<bool>> bit_genome = emp::NewPtr<TypedGenome<bool>>(32);
            genomes.push_back(bit_genome);
          }
  
          if (split_genomes[i].compare("int_genome") == 0) {
            emp::Ptr<TypedGenome<int>> int_genome = emp::NewPtr<TypedGenome<int>>(32);
            genomes.push_back(int_genome);
          }
  
          if (split_genomes[i].compare("byte_genome") == 0) {
            emp::Ptr<TypedGenome<std::byte>> byte_genome = emp::NewPtr<TypedGenome<std::byte>>(32);
            genomes.push_back(byte_genome);
          }
  
          if (split_genomes[i].compare("val_genome") == 0) {
            emp::Ptr<TypedGenome<double>> val_genome = emp::NewPtr<TypedGenome<double>>(32);
            genomes.push_back(val_genome);
          }
        }
      }
  
      void initMutProbs(emp::Random random) {
        emp::vector<std::string> split_mut_probs;
        emp::slice(SharedData().mut_probs, split_mut_probs, ',');
        for (size_t i = 0; i < split_mut_probs.size(); i++) {
          double mut_prob = std::stod(split_mut_probs[i]);
          mut_probs.push_back(mut_prob);
        }
      }
  
      void initBrains(emp::Random & random) {
        emp::vector<std::string> split_brains;
        emp::slice(SharedData().brains, split_brains, ',');
        for(size_t i = 0; i < split_brains.size(); i++) {
  
          if (split_brains[i].compare("aagos_brain") == 0) {
            emp::Ptr<AagosBrain> aagos_brain = emp::NewPtr<AagosBrain>(genomes);
            brains.push_back(aagos_brain);
          }
  
        }
      }
  
      // calls process on each brain in the brains vector, passing the DynamicOrg data map to each one
      void GenerateOutput() override {
        map.Set<emp::Ptr<Organism>>("organism", emp::Ptr<Organism>(this));
        for (size_t i = 0; i < brains.size(); i++) {
          brains[i]->process(map);
        }
      }
  
      void SetupConfig() override {
        GetManager().LinkVar(SharedData().genomes, "genomes", "Types of genomes to use.");
        GetManager().LinkVar(SharedData().mut_probs, "mut_probs", "Mutation probabilities of each genome.");
        GetManager().LinkVar(SharedData().brains, "brains", "Types of brains to use.");
        GetManager().LinkVar(SharedData().output_name, "output_name",
                        "Name of variable to contain bit sequence.");
        GetManager().LinkVar(SharedData().init_random, "init_random",
                        "Should we randomize ancestor?  (0 = all zeros)");
  
        map.AddVar<std::string>("output_name", SharedData().output_name);
        map.AddVar<emp::Ptr<Organism>>("organism", emp::Ptr<Organism>(this));
      }
  
      void SetupModule() override {
        GetManager().AddSharedTrait(SharedData().output_name,
                                    "Bitset output from organism.",
                                    emp::vector<emp::BitVector>(0));
      }
    };
  
    MABE_REGISTER_ORG_TYPE(DynamicOrg, "Dynamic organism that can take any number of genomes and brains.");
  }
  
  #endif
  