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

#include "RegionHandler.h"

#include "utility.h"

namespace aevol {
namespace gff3 {

RegionHandler::RegionHandler(std::string seqid, dna_size_type size, bool circular)
    : seqid_(seqid),
      size_(size),
      root_feature_(FeatureType::REGION,
                    1,
                    size_ + 1,
                    Strand::LEADING,
                    {{"ID", seqid}, {"Is_circular", circular ? "true" : "false"}}) {}


void RegionHandler::add_feature(const FeatureType type,
                                dna_size_type start,
                                dna_size_type size,
                                Strand strand,
                                std::optional<std::string> id,
                                std::map<std::string, std::string> attributes) {
  // If there already is an entry for the given feature...
  if (auto feature_it = features_[std::to_underlying(strand)].find({start, type});
      feature_it != features_[std::to_underlying(strand)].end()) {
    // Update pre-existing entry (merge attributes into pre-existing feature)
    feature_it->second.attributes.merge(attributes);
    for (const auto& [key, value]: attributes) {
      feature_it->second.attributes[key] += ',' + attributes[key];
    }
  } else {
    // Create new feature
    auto gff_start = (strand == Strand::LEADING) ? start : mod(start - size + 1, size_);
    auto feature = Feature{type, gff_start, gff_start + size, strand, {}};
    if (id) feature.attributes["ID"] = id.value();
    feature.attributes.merge(attributes);
    features_[std::to_underlying(strand)][{start, type}] = (std::move(feature));
  }
}

std::ostream& operator<<(std::ostream& os, const RegionHandler& region_sequence) {
  // ----- Write sequence-region directive
  os << "##sequence-region " << region_sequence.seqid() << ' '
     << region_sequence.start() << ' '
     << region_sequence.end() << "\n";

  // ----- Write root feature
  os << region_sequence.seqid() << "\t" << "Aevol" << "\t" << region_sequence.root_feature() << "\n"
     << "\n";

  // ----- Write features
  for (auto& strand: {Strand::LEADING, Strand::LAGGING}) {
    if (strand == Strand::LAGGING) os << "\n";
    os << std::format("# {} strand\n", to_string(strand));
    for (const auto& feature: region_sequence.features(strand)) {
      os << region_sequence.seqid() << "\t"
         << "Aevol" << "\t"
         << feature << "\n";
    }
  }

  return os;
}

}  // namespace gff3
}  // namespace aevol
