/**
 * \file test/test_spectree.cpp
 * \date 11/12/2023
 * \author Olivier Langella
 * \brief test spectrum int
 */


/*
 * DeepProt, Open Modification Search engine for proteomics
 * Copyright (C) 2018  Olivier Langella <olivier.langella@u-psud.fr>
 *
 * 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 3 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/>.
 *
 */

// ./tests/catch2-only-tests [spectree] -s


#include <QDebug>
#include <QString>
#include <vector>
#include <iostream>
#include <cstdio>

#include <odsstream/tsvoutputstream.h>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/processing/uimonitor/uimonitortext.h>
#include <pappsomspp/core/processing/uimonitor/uimonitorvoid.h>

#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <pappsomspp/core/exception/exceptionoutofrange.h>
#include "tests/tests-config.h"
#include "common.h"

#include <pappsomspp/core/processing/spectree/spectree.h>
#include <pappsomspp/core/processing/spectree/specxtractinterface.h>
#include <pappsomspp/core/processing/uimonitor/uimonitortextpercent.h>

TEST_CASE("Test spectree spectrum int", "[spectree]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  SECTION("..:: spectree ::..", "[spectree]")
  {
    std::cout << std::endl << "..:: DeepProt test ::.." << std::endl;
    //   OboPsiMod test;


    qDebug();
    pappso::spectree::BucketClustering bucket_clustering;
    bucket_clustering.addItemCart(pappso::spectree::ItemCart(
      0,
      std::vector<std::size_t>(
        {10008, 14711, 17111, 28417, 30817, 35521, 45528})));

    bucket_clustering.addItemCart(pappso::spectree::ItemCart(
      1,
      std::vector<std::size_t>(
        {10008, 17111, 17512, 30817, 31218, 38221, 48329})));


    bucket_clustering.addItemCart(pappso::spectree::ItemCart(
      2,
      std::vector<std::size_t>(
        {10008, 14711, 17111, 26020, 28420, 33123, 43131})));


    bucket_clustering.addItemCart(pappso::spectree::ItemCart(
      3,
      std::vector<std::size_t>(
        {14711, 16707, 23511, 28417, 35521, 37219, 51928})));


    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(4,
                                 std::vector<std::size_t>({7104,
                                                           14711,
                                                           17111,
                                                           26512,
                                                           28417,
                                                           30817,
                                                           35521,
                                                           41219,
                                                           43131,
                                                           45528})));

    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(5,
                                 std::vector<std::size_t>({10008,
                                                           17111,
                                                           18913,
                                                           25915,
                                                           28417,
                                                           30817,
                                                           30917,
                                                           32619,
                                                           39722,
                                                           49730})));


    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(6,
                                 std::vector<std::size_t>({7104,
                                                           10008,
                                                           17111,
                                                           18913,
                                                           29717,
                                                           32416,
                                                           34218,
                                                           39722,
                                                           41321,
                                                           51329})));


    std::vector<pappso::spectree::Bucket> bucket_list =
      bucket_clustering.asSortedList();

    REQUIRE(bucket_list.size() == 11);


    std::vector<std::size_t> bucket0  = {0, 1, 2, 4, 5, 6};
    std::vector<std::size_t> bucket1  = {0, 1, 2, 5, 6};
    std::vector<std::size_t> bucket2  = {0, 1, 4, 5};
    std::vector<std::size_t> bucket3  = {0, 2, 3, 4};
    std::vector<std::size_t> bucket4  = {0, 3, 4};
    std::vector<std::size_t> bucket5  = {0, 3, 4, 5};
    std::vector<std::size_t> bucket6  = {0, 4};
    std::vector<std::size_t> bucket7  = {2, 4};
    std::vector<std::size_t> bucket8  = {4, 6};
    std::vector<std::size_t> bucket9  = {5, 6};
    std::vector<std::size_t> bucket10 = {5, 6};

    REQUIRE(bucket_list[0].getId() == 17111);
    INFO("mass 17111 is present in spectrum 0, 1, 2, 4, 5, 6");
    REQUIRE_THAT(bucket_list[0].getCartList(),
                 Catch::Matchers::Equals(bucket0));


    REQUIRE(bucket_list[1].getId() == 10008);
    REQUIRE_THAT(bucket_list[1].getCartList(),
                 Catch::Matchers::Equals(bucket1));


    REQUIRE(bucket_list[2].getId() == 30817);
    REQUIRE_THAT(bucket_list[2].getCartList(),
                 Catch::Matchers::Equals(bucket2));

    REQUIRE(bucket_list[3].getId() == 14711);
    REQUIRE_THAT(bucket_list[3].getCartList(),
                 Catch::Matchers::Equals(bucket3));

    REQUIRE(bucket_list[4].getId() == 35521);
    REQUIRE_THAT(bucket_list[4].getCartList(),
                 Catch::Matchers::Equals(bucket4));

    REQUIRE(bucket_list[5].getId() == 28417);
    REQUIRE_THAT(bucket_list[5].getCartList(),
                 Catch::Matchers::Equals(bucket5));



    REQUIRE(bucket_list[6].getId() == 45528);

    INFO("mass 45528 is present in spectrum 1, 5");
    REQUIRE_THAT(bucket_list[6].getCartList(),
                 Catch::Matchers::Equals(bucket6));


    REQUIRE(bucket_list[7].getId() == 43131);
    REQUIRE_THAT(bucket_list[7].getCartList(),
                 Catch::Matchers::Equals(bucket7));

    REQUIRE(bucket_list[8].getId() == 7104);
    REQUIRE_THAT(bucket_list[8].getCartList(),
                 Catch::Matchers::Equals(bucket8));

    REQUIRE(bucket_list[9].getId() == 18913);
    REQUIRE_THAT(bucket_list[9].getCartList(),
                 Catch::Matchers::Equals(bucket9));

    REQUIRE(bucket_list[10].getId() == 39722);
    REQUIRE_THAT(bucket_list[10].getCartList(),
                 Catch::Matchers::Equals(bucket10));


    qDebug();
    REQUIRE(bucket_clustering.getItemCartCount() == 7);
    pappso::spectree::SpecTree spec_trees(bucket_clustering);


    qDebug();

    REQUIRE_THAT(spec_trees.getSpectrumFirstNodeIndex(),
                 Catch::Matchers::Equals(
                   std::vector<std::size_t>({0, 1, 2, 11, 3, 4, 5})));
    qDebug() << spec_trees.toString();

    INFO("Spectree Extraction");
    pappso::spectree::SpecXtractMap xtract_map;
    QTextStream output(stdout, QIODevice::WriteOnly);
    UiMonitorTextPercent monitor(output);

    REQUIRE_THROWS_AS(spec_trees.xtract(monitor, xtract_map,0, 7, 6, 5, 0),
                      pappso::ExceptionOutOfRange);

    xtract_map.clear();
    spec_trees.xtract(monitor, xtract_map,0, 6, 0, 5, 0);
    REQUIRE(xtract_map.getMapSimilarities().size() == 6);
    auto map_count = xtract_map.getMapSimilarities().at(6);

    REQUIRE_THAT(
      getMapKeys(map_count),
      Catch::Matchers::Equals(std::vector<std::size_t>({0, 1, 2, 4, 5})));

    REQUIRE_THAT(
      getMapValues(map_count),
      Catch::Matchers::Equals(std::vector<std::size_t>({2, 2, 2, 2, 4})));


    pappso::spectree::SpecXtractMap xtract_map2;
    spec_trees.xtract(monitor, xtract_map2,0, 6, 0, 5, 3);
    map_count = xtract_map2.getMapSimilarities().at(6);

    REQUIRE_THAT(getMapKeys(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({4, 5})));

    REQUIRE_THAT(getMapValues(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({2, 4})));


    REQUIRE(spec_trees.extractSpectrumPairSimilarityCount(6, 5) == 4);
    REQUIRE(spec_trees.extractSpectrumPairSimilarityCount(5, 6) == 4);


    pappso::spectree::SpecXtractMap xtract_map3;
    spec_trees.xtract(monitor, xtract_map3,0, 3, 0, 2, 0);
    map_count = xtract_map3.getMapSimilarities().at(3);

    REQUIRE_THAT(getMapKeys(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({0, 2})));

    REQUIRE_THAT(getMapValues(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({3, 1})));

    // REQUIRE(simil2.m_matrix[6][5] == 4);
    // REQUIRE(simil2.m_matrix[3][2] == 4);
  }

  SECTION("..:: spectree publi ::..", "[spectree]")
  {
    // https://pubs.acs.org/doi/10.1021/acs.jproteome.7b00308

    qDebug();

    pappso::spectree::BucketClustering bucket_clustering;
    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(0, std::vector<std::size_t>({2, 4, 6, 7})));
    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(1, std::vector<std::size_t>({1, 3, 4, 5, 7})));
    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(2, std::vector<std::size_t>({1, 2, 3, 5, 6})));
    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(3, std::vector<std::size_t>({1, 3, 4, 6, 7})));
    bucket_clustering.addItemCart(
      pappso::spectree::ItemCart(4, std::vector<std::size_t>({1, 2, 5, 7})));

    std::vector<pappso::spectree::Bucket> bucket_list =
      bucket_clustering.asSortedList();

    REQUIRE(bucket_list.size() == 7);


    std::vector<std::size_t> bucket0 = {0, 1, 3};
    std::vector<std::size_t> bucket1 = {0, 1, 3, 4};
    std::vector<std::size_t> bucket2 = {0, 2, 3};
    std::vector<std::size_t> bucket3 = {0, 2, 4};
    std::vector<std::size_t> bucket4 = {1, 2, 3};
    std::vector<std::size_t> bucket5 = {1, 2, 3, 4};
    std::vector<std::size_t> bucket6 = {1, 2, 4};
    REQUIRE(bucket_list[0].getId() == 4);
    INFO("mass 17111 is present in spectrum 1, 2, 3, 5, 6, 7");
    REQUIRE_THAT(bucket_list[0].getCartList(),
                 Catch::Matchers::Equals(bucket0));


    REQUIRE(bucket_list[1].getId() == 7);
    REQUIRE_THAT(bucket_list[1].getCartList(),
                 Catch::Matchers::Equals(bucket1));


    REQUIRE(bucket_list[2].getId() == 6);
    REQUIRE_THAT(bucket_list[2].getCartList(),
                 Catch::Matchers::Equals(bucket2));

    REQUIRE(bucket_list[3].getId() == 2);
    REQUIRE_THAT(bucket_list[3].getCartList(),
                 Catch::Matchers::Equals(bucket3));

    REQUIRE(bucket_list[4].getId() == 3);
    REQUIRE_THAT(bucket_list[4].getCartList(),
                 Catch::Matchers::Equals(bucket4));

    REQUIRE(bucket_list[5].getId() == 1);
    REQUIRE_THAT(bucket_list[5].getCartList(),
                 Catch::Matchers::Equals(bucket5));


    REQUIRE(bucket_list[6].getId() == 5);

    INFO("mass 45528 is present in spectrum 1, 5");
    REQUIRE_THAT(bucket_list[6].getCartList(),
                 Catch::Matchers::Equals(bucket6));

    REQUIRE(bucket_clustering.getItemCartCount() == 5);
    pappso::spectree::SpecTree spec_trees(bucket_clustering);


    REQUIRE_THAT(
      spec_trees.getSpectrumFirstNodeIndex(),
      Catch::Matchers::Equals(std::vector<std::size_t>({0, 1, 4, 2, 3})));
    qDebug() << spec_trees.toString();

    QTextStream output(stdout, QIODevice::WriteOnly);
    UiMonitorTextPercent monitor(output);
    pappso::spectree::SpecXtractMap xtract_map;
    spec_trees.xtract(monitor, xtract_map,0, 4, 0, 3, 0);
    auto map_count = xtract_map.getMapSimilarities().at(4);
    REQUIRE_THAT(
      getMapKeys(map_count),
      Catch::Matchers::Equals(std::vector<std::size_t>({0, 1, 2, 3})));
    REQUIRE_THAT(
      getMapValues(map_count),
      Catch::Matchers::Equals(std::vector<std::size_t>({2, 3, 3, 2})));

    map_count = xtract_map.getMapSimilarities().at(3);
    REQUIRE_THAT(getMapKeys(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({0, 1, 2})));
    REQUIRE_THAT(getMapValues(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({3, 4, 3})));


    map_count = xtract_map.getMapSimilarities().at(2);
    REQUIRE_THAT(getMapKeys(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({0, 1})));
    REQUIRE_THAT(getMapValues(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({2, 3})));

    map_count = xtract_map.getMapSimilarities().at(1);
    REQUIRE_THAT(getMapKeys(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({0})));
    REQUIRE_THAT(getMapValues(map_count),
                 Catch::Matchers::Equals(std::vector<std::size_t>({2})));
  }
}
