// Copyright (c) 2020 Robert Vaser

#include "bioparser/sam_parser.hpp"

#include <numeric>
#include <string>

#include "biosoup/overlap.hpp"
#include "gtest/gtest.h"

namespace bioparser {
namespace test {

struct SamOverlap: public biosoup::Overlap {
 public:
  SamOverlap(
      const char* q_name, std::uint32_t q_name_len,
      std::uint32_t flag,
      const char* t_name, std::uint32_t t_name_len,
      std::uint32_t t_begin,
      std::uint32_t map_quality,
      const char* cigar, std::uint32_t cigar_len,
      const char* t_next_name, std::uint32_t t_next_name_len,
      std::uint32_t t_next_begin,
      std::uint32_t template_len,
      const char* data, std::uint32_t data_len,
      const char* quality, std::uint32_t quality_len)
      : biosoup::Overlap(
          0, 0, 0,
          0, t_begin, 0,
          0,
          cigar, cigar_len),
        q_name(q_name, q_name_len),
        flag(flag),
        t_name(t_name, t_name_len),
        map_quality(map_quality),
        t_next_name(t_next_name, t_next_name_len),
        t_next_begin(t_next_begin),
        template_len(template_len),
        data(data, data_len),
        quality(quality, quality_len) {}

  std::string q_name;
  std::uint32_t flag;
  std::string t_name;
  std::uint32_t map_quality;
  std::string t_next_name;
  std::uint32_t t_next_begin;
  std::uint32_t template_len;
  std::string data;
  std::string quality;
};

class BioparserSamTest: public ::testing::Test {
 public:
  void Setup(const std::string& file) {
    p = Parser<SamOverlap>::Create<SamParser>(TEST_DATA + file);
  }

  void Check() {
    EXPECT_EQ(48, o.size());
    EXPECT_EQ(795237, std::accumulate(o.begin(), o.end(), 0,
        [] (std::uint32_t s, const std::unique_ptr<SamOverlap>& it) {
          return s +
              it->q_name.size() +
              it->t_name.size() +
              it->alignment.size() +
              it->t_next_name.size() +
              it->data.size() +
              it->quality.size();
        }));
    EXPECT_EQ(639677, std::accumulate(o.begin(), o.end(), 0,
        [] (std::uint32_t s, const std::unique_ptr<SamOverlap>& it) {
          return s +
              it->flag +
              it->rhs_begin +
              it->map_quality +
              it->t_next_begin +
              it->template_len;
        }));
  }

  std::unique_ptr<Parser<SamOverlap>> p;
  std::vector<std::unique_ptr<SamOverlap>> o;
};

TEST_F(BioparserSamTest, ParseWhole) {
  Setup("sample.sam");
  o = p->Parse(-1);
  Check();
}

TEST_F(BioparserSamTest, ParseInChunks) {
  Setup("sample.sam");
  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
    o.insert(
        o.end(),
        std::make_move_iterator(t.begin()),
        std::make_move_iterator(t.end()));
  }
  Check();
}

TEST_F(BioparserSamTest, FormatError) {
  Setup("sample.paf");
  try {
    o = p->Parse(-1);
  } catch (std::invalid_argument& exception) {
    EXPECT_STREQ(
        exception.what(),
        "[bioparser::SamParser] error: invalid file format");
  }
}

TEST_F(BioparserSamTest, CompressedParseWhole) {
  Setup("sample.sam.gz");
  o = p->Parse(-1);
  Check();
}

TEST_F(BioparserSamTest, CompressedParseInChunks) {
  Setup("sample.sam.gz");
  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
    o.insert(
        o.end(),
        std::make_move_iterator(t.begin()),
        std::make_move_iterator(t.end()));
  }
  Check();
}

TEST_F(BioparserSamTest, CompressedFormatError) {
  Setup("sample.paf.gz");
  try {
    o = p->Parse(-1);
  } catch (std::invalid_argument& exception) {
    EXPECT_STREQ(
        exception.what(),
        "[bioparser::SamParser] error: invalid file format");
  }
}

}  // namespace test
}  // namespace bioparser
