// Copyright (C) 2018  INRIA
//
// Treerecs is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Treerecs 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "gtest/gtest.h"
#include "examples_paths.h"

#include <treerecs/Constants.h>
#include <treerecs/containers/BipartitionList.h>
#include <treerecs/tools/IO/IO.h>
#include <treerecs/tools/PhyloTreeToolBox.h>
#include <treerecs/tools/utils.h>

using namespace treerecs;

class bipartitionList_test : public testing::Test {
protected:
  virtual void SetUp() {
    tiny_trees = IO::readTreesFile(
        bipartition_list_path + "bipartition_tree_test_0.txt"
        , TextFormat::newick, -1, false, false, 0, false);

    large_tree = IO::readTreeFile(
        ensembl_examples_path + "phyml_tree.txt"
        , TextFormat::newick, false, false, false);
  }

  virtual void TearDown() {}

  std::vector<std::shared_ptr<bpp::PhyloTree>> tiny_trees;

  std::shared_ptr<bpp::PhyloTree> large_tree;
};

TEST_F(bipartitionList_test, simple_run) {
  bool verbose = false;

  auto tree0 = tiny_trees[0];
  auto tree1 = tiny_trees[1];
  auto tree2 = tiny_trees[2];

  if (verbose) {
    std::cout << "> Using tree: " << std::endl;
    std::cout << *tree1;
    std::cout << std::endl;
  }

  // Create bipartition list
  BipartitionList bipartitionList_original = BipartitionList(*tree1);

  // Test if all bipartitions are compatible
  ASSERT_TRUE(bipartitionList_original.areAllCompatible());

  // Generate a matrix according to the structure
  auto matrix =  bipartitionList_original.toMatrix();

  // Re-construct the tree.
  auto resulting_tree = bipartitionList_original.toTree(false);

  if (verbose) {
    std::cout << "> Resulting tree is:" << std::endl;
    std::cout << *resulting_tree << std::endl;
  }
}

TEST_F(bipartitionList_test, test_BipartionList_on_tiny_tree) {
  auto tree0 = tiny_trees[0];
  auto tree1 = tiny_trees[1];
  auto tree2 = tiny_trees[2];

  // Create bipartition list
  BipartitionList bipartitionList_original = BipartitionList(*tree2);
  ASSERT_TRUE(bipartitionList_original.areAllCompatible());

  // Re-construct the tree.
  auto resulting_tree = bipartitionList_original.toTree(false);
  // std::cout << "Original tree: " << *tree2 << std::endl;
  // std::cout << "Clone    tree: " << *resulting_tree << std::endl;

  // Test if a new bipartition list from the resulting tree is equivalent to the
  // previous one.
  BipartitionList bipartitionList_clone = BipartitionList(*resulting_tree);
  ASSERT_TRUE(bipartitionList_clone.areAllCompatible());

  /*
  std::cout << "Original bipartition list:" << std::endl
            << bipartitionList_original << std::endl;
  std::cout << "Clone    bipartition list:" << std::endl
            << bipartitionList_clone << std::endl;
  */

  // Test if all original bipartitions are compatible with clones.
  for (decltype(bipartitionList_original.getNumberOfBipartitions()) i = 0 ;
      i < bipartitionList_original.getNumberOfBipartitions() ;
      i++) {
    auto original_bipartition = bipartitionList_original.getBipartition(i);
    ASSERT_TRUE(
        bipartitionList_clone.haveSameElementsThan(original_bipartition)
    );

    ASSERT_TRUE(
        bipartitionList_clone.containsBipartition(original_bipartition)
    );

    ASSERT_TRUE(
        bipartitionList_clone.areAllCompatibleWith(original_bipartition)
    );
  }
}

TEST_F(bipartitionList_test, test_BipartionList_on_large_tree) {

  // Create bipartition list
  BipartitionList bipartitionList_original = BipartitionList(*large_tree);
  ASSERT_TRUE(bipartitionList_original.areAllCompatible());

  // Re-construct the tree.
  auto resulting_tree = bipartitionList_original.toTree(false);
  // std::cout << "Original tree: " << *tree2 << std::endl;
  // std::cout << "Clone    tree: " << *resulting_tree << std::endl;

  // Test if a new bipartition list from the resulting tree is equivalent to the
  // previous one.
  BipartitionList bipartitionList_clone = BipartitionList(*resulting_tree);
  ASSERT_TRUE(bipartitionList_clone.areAllCompatible());

  /*
  std::cout << "Original bipartition list:" << std::endl
            << bipartitionList_original << std::endl;
  std::cout << "Clone    bipartition list:" << std::endl
            << bipartitionList_clone << std::endl;
  */

  // Test if all original bipartitions are compatible with clones.
  for (decltype(bipartitionList_original.getNumberOfBipartitions()) i = 0 ;
      i < bipartitionList_original.getNumberOfBipartitions() ;
      i++) {
    auto original_bipartition = bipartitionList_original.getBipartition(i);
    ASSERT_TRUE(
        bipartitionList_clone.haveSameElementsThan(original_bipartition)
    );

    ASSERT_TRUE(
        bipartitionList_clone.containsBipartition(original_bipartition)
    );

    ASSERT_TRUE(
        bipartitionList_clone.areAllCompatibleWith(original_bipartition)
    );
  }
}
