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

#ifndef AEVOL_MUTATION_EVENT_H
#define AEVOL_MUTATION_EVENT_H

#include <cstdint>
#include <iostream>
#include <memory>
#include <string>

#include "Dna.h"

namespace aevol {

class MutationEvent {
 public:
  enum class Type {
    SWITCH          = 0,
    SMALL_INSERTION = 1,
    SMALL_DELETION  = 2,
    DUPLICATION     = 3,
    DELETION        = 4,
    TRANSLOCATION   = 5,
    INVERSION       = 6
  };
  using size_type = Dna::size_type;

 protected:
  MutationEvent() = default;

 public:
  ~MutationEvent();

  static auto make_switch(size_type pos) -> std::unique_ptr<MutationEvent>;                 // 2b version
  static auto make_switch(size_type pos, int8_t offset) -> std::unique_ptr<MutationEvent>;  // 4b version

  static auto make_small_insertion(size_type pos, size_type number, char* seq) -> std::unique_ptr<MutationEvent>;
  static auto make_small_deletion(size_type pos, size_type number) -> std::unique_ptr<MutationEvent>;

  static auto make_large_duplication(size_type pos1, size_type pos2, size_type pos3) -> std::unique_ptr<MutationEvent>;
  static auto make_large_deletion(size_type pos1, size_type pos2) -> std::unique_ptr<MutationEvent>;
  static auto make_translocation(
      size_type pos1, size_type pos2, size_type pos3, size_type pos4, bool invert) -> std::unique_ptr<MutationEvent>;
  static auto make_inversion(size_type pos1, size_type pos2) -> std::unique_ptr<MutationEvent>;

  auto type() const { return type_; };

  auto pos_1() const { return pos_1_; }
  auto pos_2() const { return pos_2_; }
  auto pos_3() const { return pos_3_; }
  auto pos_4() const { return pos_4_; }

  auto number() const { return number_; }

  auto invert() const { return invert_; }

  const auto* seq() const { return seq_; }

  #ifdef BASE_4
  auto offset() const { return offset_; }
  #endif

 private:
  // Type of the mutation
  Type type_;

  // Positions characterizing the mutation (most mutation types don't use all of these)
  size_type pos_1_ = -1;
  size_type pos_2_ = -1;
  size_type pos_3_ = -1;
  size_type pos_4_ = -1;

  // Number of inserted or deleted bases
  size_type number_;

  bool invert_;

  char* seq_ = nullptr;

  #ifdef BASE_4
  int8_t offset_;  // base offset for a switch (logics: new_base = old_base + offset_ % 4)
  #endif
};

using MutationType = MutationEvent::Type;

inline std::string to_string(const MutationType& t) {
  switch (t) {
    case MutationType::SWITCH:          return "SWITCH";
    case MutationType::SMALL_INSERTION: return "SMALL_INSERTION";
    case MutationType::SMALL_DELETION:  return "SMALL_DELETION";
    case MutationType::DUPLICATION:     return "DUPLICATION";
    case MutationType::DELETION:        return "DELETION";
    case MutationType::TRANSLOCATION:   return "TRANSLOCATION";
    case MutationType::INVERSION:       return "INVERSION";
    default:                            return "UNKNOWN";
  }
}

inline MutationType to_MutationType(const std::string& str) {
  if (str == "SWITCH")
    return MutationType::SWITCH;
  else if (str == "SMALL_INSERTION")
    return MutationType::SMALL_INSERTION;
  else if (str == "SMALL_DELETION")
    return MutationType::SMALL_DELETION;
  else if (str == "DUPLICATION")
    return MutationType::DUPLICATION;
  else if (str == "DELETION")
    return MutationType::DELETION;
  else if (str == "TRANSLOCATION")
    return MutationType::TRANSLOCATION;
  else if (str == "INVERSION")
    return MutationType::INVERSION;
  else {
    std::cerr << "unexpected MutationType descriptor string " << str << std::endl;
    exit(1);
  }
}

}  // namespace aevol

#endif  // AEVOL_MUTATION_EVENT_H
