// ****************************************************************************
//
//          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 <cstdlib>
#include <sstream>

#include "MutationReport.h"
#include "PointMutation.h"
#include "SmallInsertion.h"
#include "SmallDeletion.h"
#include "Duplication.h"
#include "Deletion.h"
#include "Translocation.h"
#include "Inversion.h"
#include "InsertionHT.h"
#include "ReplacementHT.h"
#include "utils.h"
#include "utility.h"

namespace aevol {

auto MutationReport::Load(gzFile tree_file) -> std::unique_ptr<MutationReport> {
  // Retrieve mutation type
  int8_t tmp_mut_type;
  gzread(tree_file, &tmp_mut_type,  sizeof(tmp_mut_type));
  MutationReport::Type mut_type = (MutationReport::Type) tmp_mut_type;

  // Call the appropriate constructor accordingly
  std::unique_ptr<MutationReport> mut;

  switch (mut_type) {
    case MutationReport::Type::SWITCH :
      mut = std::make_unique<PointMutation>();
      break;
    case MutationReport::Type::S_INS :
      mut = std::make_unique<SmallInsertion>();
      break;
    case MutationReport::Type::S_DEL :
      mut = std::make_unique<SmallDeletion>();
      break;
    case MutationReport::Type::DUPL :
      mut = std::make_unique<Duplication>();
      break;
    case MutationReport::Type::DEL :
      mut = std::make_unique<Deletion>();
      break;
    case MutationReport::Type::TRANS :
      mut = std::make_unique<Translocation>();
      break;
    case MutationReport::Type::INV :
      mut = std::make_unique<Inversion>();
      break;
    case MutationReport::Type::INS_HT :
      mut = std::make_unique<InsertionHT>();
      break;
    case MutationReport::Type::REPL_HT :
      mut = std::make_unique<ReplacementHT>();
      break;
    default :
      exit_with_dev_msg("invalid mutation type ", __FILE__, __LINE__);
      exit(-1); // Superfluous but suppresses a warning
  }

  // Load from file
  mut->load(tree_file);
  return mut;
}

std::ostream& operator<<(std::ostream& os, const MutationReport& o) {
  os << std::to_underlying(o.mut_type()) << ','
         << o.pos1() << ','
         << o.pos2() << ','
         << o.pos3() << ','
         << o.pos4() << ','
         << o.sequence() << ','
         << static_cast<int>(o.invert()) << ','
         << o.align_score() << ','
         << o.align_score_2() << ','
         << o.length() << ','
         << o.repl_seg_len();
    return os;
}

} // namespace aevol
