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




// =================================================================
//                              Includes
// =================================================================
#include <gtest/gtest.h>

#include <array>
#include <format>
#include <iterator>
#include <memory>
#include <string>
#include <sstream>

#include "Individual.h"
#include "macros.h"
#include "Strand.h"

using namespace aevol;

//############################################################################
//                                                                           #
//                         Class IndividualTest                              #
//                                                                           #
//############################################################################
class Dna_4b_Test: public testing::Test {
 protected:
  virtual void SetUp(void);
  virtual void TearDown(void);

  std::vector<std::string> genomes;
  std::vector<std::unique_ptr<Individual>> indivs;
};

// ===========================================================================
//                                 Public Methods
// ===========================================================================
void Dna_4b_Test::SetUp(void) {
  // Build ad-hoc dna
  //
  // dna1: AS + prom + AS + AG + AS + term + AS + prom + AS
  //
  // AS = Arbitrary Sequence
  // AG = Arbitrary Gene
  // Do not modify the sequences !

  // Define arbitrary sequences
  std::array<std::string, 5> as = {
    "12032",
    "3320",
    "3301203",
    "002301",
    "20112322013203210"
  };

  // Define an arbitrary gene
  std::array<std::string, 2> gene = {
      std::string("301212") + "2102" + "013310231022333013" + "130",
      std::string("301212") + "0122" + "013230022233133023322" + "103"
  };

  // Define an arbitrary terminator
  std::string term = "11320003200";
  std::string pseudoterm = "1200001303"; // creates a term if put just after a '2' (e.g. after prom[1])

  // Define a few arbitrary promoters
  std::array<std::string, 2> prom = {
      "3322222010310333112023", // dist from consensus: 2
      "3022022011312333112012"  // dist from consensus: 4
  };

  // Build genomes
  genomes.push_back(std::string(as[0] + prom[0] + as[1] + prom[1] + gene[0] + as[2] + term + as[3] + prom[1] + as[4]));
  genomes.push_back(std::string(as[0] + prom[0] + prom[1] + gene[1] + as[2] + prom[1] + term + as[3] + prom[1] + as[4]));
  genomes.push_back(std::string(as[1] + prom[1] + pseudoterm + prom[0] + gene[0] + term + as[0]));

  for (const auto& genome : genomes) {
    indivs.push_back(Individual::make_from_sequence(genome));
  }
}

void Dna_4b_Test::TearDown() {
}

TEST_F(Dna_4b_Test, TestFarthestGenome0) {
  auto i = 0;
  EXPECT_EQ(genomes[i].size(), indivs[i]->dna().length());

  indivs[i]->locate_promoters();
  const auto& promoters = indivs[i]->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 3);
  auto cur_prom = promoters[Strand::LEADING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 5);
  EXPECT_EQ(cur_prom->nb_errors(), 2);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 31);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 108);
  EXPECT_EQ(cur_prom->nb_errors(), 4);

  indivs[i]->prom_compute_RNA();
  const auto& rnas = indivs[i]->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).end, 101);
  EXPECT_EQ((*rna_it).length(), 119);

  indivs[i]->start_protein();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 53);

  indivs[i]->compute_protein();
  const auto& proteins = indivs[i]->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 66);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indivs[i]->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 66);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}

TEST_F(Dna_4b_Test, TestFarthestGenome0Inverted) {
  auto i = 0;

  // Build inverted indiv (with the same genome but inverted and cut at an arbitrary position)
  auto genome = indivs[i]->dna().subseq(42, indivs[i]->dna().length(), Strand::LAGGING);
  auto indiv = Individual::make_from_sequence(genome);

  indiv->locate_promoters();
  const auto& promoters = indiv->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 3);
  auto cur_prom = promoters[Strand::LAGGING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 81);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 37);
  EXPECT_EQ(cur_prom->nb_errors(), 2);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 11);
  EXPECT_EQ(cur_prom->nb_errors(), 4);

  indiv->prom_compute_RNA();
  const auto& rnas = indiv->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).length(), 119);
  EXPECT_EQ((*rna_it).end, 88);

  indiv->start_protein();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 136);

  indiv->compute_protein();
  const auto& proteins = indiv->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 123);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indiv->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 123);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}

TEST_F(Dna_4b_Test, TestFarthestGenome1) {
  auto i = 1;
  EXPECT_EQ(genomes[i].size(), indivs[i]->dna().length());
  indivs[i]->locate_promoters();
  const auto& promoters = indivs[i]->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 4);
  auto cur_prom = promoters[Strand::LEADING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 5);
  EXPECT_EQ(cur_prom->nb_errors(), 2);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 27);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 90);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 129);
  EXPECT_EQ(cur_prom->nb_errors(), 4);

  indivs[i]->prom_compute_RNA();
  const auto& rnas = indivs[i]->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).end, 122);
  EXPECT_EQ((*rna_it).length(), 140);

  indivs[i]->start_protein();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 49);

  indivs[i]->compute_protein();
  const auto& proteins = indivs[i]->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 62);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indivs[i]->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 62);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}

TEST_F(Dna_4b_Test, TestFarthestGenome1Inverted) {
  auto i = 1;

  // Build inverted indiv (with the same genome but inverted and cut at an arbitrary position)
  auto genome = indivs[i]->dna().subseq(100, indivs[i]->dna().length(), Strand::LAGGING);
  auto indiv = Individual::make_from_sequence(genome);

  indiv->locate_promoters();
  const auto& promoters = indiv->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 4);
  auto cur_prom = promoters[Strand::LAGGING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 139);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 95);
  EXPECT_EQ(cur_prom->nb_errors(), 2);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 73);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 10);
  EXPECT_EQ(cur_prom->nb_errors(), 4);

  indiv->prom_compute_RNA();
  const auto& rnas = indiv->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).end, 146);
  EXPECT_EQ((*rna_it).length(), 140);

  indiv->start_protein();
  rna_it = rnas.begin();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 51);

  indiv->compute_protein();
  const auto& proteins = indiv->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 38);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indiv->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 38);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}

TEST_F(Dna_4b_Test, TestFarthestGenome2) {
  auto i = 2;
  EXPECT_EQ(genomes[i].size(), indivs[i]->dna().length());
  indivs[i]->locate_promoters();
  const auto& promoters = indivs[i]->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 2);
  auto cur_prom = promoters[Strand::LEADING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 4);
  EXPECT_EQ(cur_prom->nb_errors(), 4);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 36);
  EXPECT_EQ(cur_prom->nb_errors(), 2);

  indivs[i]->prom_compute_RNA();
  const auto& rnas = indivs[i]->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).end, 99);
  EXPECT_EQ((*rna_it).length(), 74);

  indivs[i]->start_protein();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 58);

  indivs[i]->compute_protein();
  const auto& proteins = indivs[i]->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 71);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indivs[i]->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 71);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}

TEST_F(Dna_4b_Test, TestFarthestGenome2Inverted) {
  auto i = 2;

  // Build inverted indiv (with the same genome but inverted and cut at an arbitrary position)
  auto genome = indivs[i]->dna().subseq(35, indivs[i]->dna().length(), Strand::LAGGING);
  auto indiv = Individual::make_from_sequence(genome);

  indiv->locate_promoters();
  const auto& promoters = indiv->annotated_chromosome().promoters();
  EXPECT_EQ(promoters.promoter_count(), 2);
  auto cur_prom = promoters[Strand::LAGGING].cbegin();
  EXPECT_EQ(cur_prom->pos(), 104);
  EXPECT_EQ(cur_prom->nb_errors(), 2);
  ++cur_prom;
  EXPECT_EQ(cur_prom->pos(), 31);
  EXPECT_EQ(cur_prom->nb_errors(), 4);

  indiv->prom_compute_RNA();
  const auto& rnas = indiv->rnas();
  ASSERT_EQ(rnas.size(), 1);
  auto rna_it = rnas.begin();
  EXPECT_FLOAT_EQ((*rna_it).e, 1 - static_cast<double>(4) / (8 + 1));
  EXPECT_EQ((*rna_it).end, 41);
  EXPECT_EQ((*rna_it).length(), 74);

  indiv->start_protein();
  EXPECT_EQ((*rna_it).rbs_positions_.size(), 1);
  EXPECT_EQ((*rna_it).rbs_positions_.front(), 82);

  indiv->compute_protein();
  const auto& proteins = indiv->proteins();
  ASSERT_EQ(proteins.size(), 1);
  auto prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 69);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));

  indiv->translate_protein(0.01);
  prot_it = proteins.begin();
  EXPECT_EQ((*prot_it)->is_duplicate(), false);
  EXPECT_EQ((*prot_it)->position_first_aa(), 69);
  EXPECT_FLOAT_EQ((*prot_it)->e(), 1 - static_cast<double>(4) / (8 + 1));
}
