// ****************************************************************************
//
//          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 "TranscriptionTerminationSequence.h"

#include "ExpSetup.h"
#include "macros.h"

namespace aevol {

bool TranscriptionTerminationSequence::is_terminator(const Dna& dna, Dna::size_type pos, Strand strand) {
  return is_stem_loop(dna, pos, strand);
}

bool TranscriptionTerminationSequence::is_stem_loop(const Dna& dna, Dna::size_type pos, Strand strand) {
  auto dna_len = dna.length();
  auto dna_data = dna.data();

  #if defined(__INTEL_COMPILER)
  __declspec(align(dna_data));
  #elif defined(__INTEL_LLVM_COMPILER)
  void* vec_r = __builtin_assume_aligned(dna_data,64);
  #endif

  if (strand == Strand::LEADING) {
    if (exp_setup->linear_chrsm() and pos > dna_len - TERM_SIZE) { // No room for a terminator
      return false;
    }

    int8_t term_dist_leading = 0;

    // ANNOTATE_SITE_BEGIN(leading_motif_loop);
    if (pos + TERM_SIZE - 1 < dna_len && pos + TERM_SIZE - 1 >= 0) {
      #pragma omp simd
      for (int32_t t_motif_id = 0 ; t_motif_id < TERM_STEM_SIZE ; ++t_motif_id) {
        int32_t pos_begin = pos + t_motif_id;
        int32_t pos_end   = pos - t_motif_id + TERM_SIZE - 1;

        // ANNOTATE_ITERATION_TASK(searchLeading);
        term_dist_leading += is_complementary_base(dna_data[pos_begin], dna_data[pos_end]) ? 1 : 0;
      }
    } else {
      for (int32_t t_motif_id = 0 ; t_motif_id < TERM_STEM_SIZE ; ++t_motif_id) {
        int32_t pos_begin = pos + t_motif_id;
        int32_t pos_end   = pos - t_motif_id + TERM_SIZE - 1;

        while (pos_begin < 0)        pos_begin += dna_len;
        while (pos_begin >= dna_len) pos_begin -= dna_len;

        while (pos_end < 0)        pos_end += dna_len;
        while (pos_end >= dna_len) pos_end -= dna_len;

        // ANNOTATE_ITERATION_TASK(searchLeading);
        term_dist_leading += is_complementary_base(dna_data[pos_begin], dna_data[pos_end]) ? 1 : 0;
      }
    }
    // ANNOTATE_SITE_END(leading_motif_loop);

    return (term_dist_leading == TERM_STEM_SIZE);
  }
  else {
    if (exp_setup->linear_chrsm() and pos < TERM_SIZE - 1) { // No room for a terminator
      return false;
    }

    int8_t term_dist_lagging = 0;

    // ANNOTATE_SITE_BEGIN(lagging_motif_loop);
    if (pos - (TERM_SIZE - 1) < dna_len && pos - (TERM_SIZE - 1) >= 0) {
      #pragma omp simd
      for (int32_t t_motif_id = 0 ; t_motif_id < TERM_STEM_SIZE ; t_motif_id++) {
        int32_t pos_begin = pos - t_motif_id;
        int32_t pos_end   = pos + t_motif_id - (TERM_SIZE - 1);

        // ANNOTATE_ITERATION_TASK(searchLeading);
        term_dist_lagging += is_complementary_base(dna_data[pos_begin], dna_data[pos_end]) ? 1 : 0;
      }
    } else {
      for (int32_t t_motif_id = 0 ; t_motif_id < TERM_STEM_SIZE ; ++t_motif_id) {
        int32_t pos_begin = pos - t_motif_id;
        int32_t pos_end   = pos + t_motif_id - (TERM_SIZE - 1);

        while (pos_begin < 0)        pos_begin += dna_len;
        while (pos_begin >= dna_len) pos_begin -= dna_len;

        while (pos_end < 0)        pos_end += dna_len;
        while (pos_end >= dna_len) pos_end -= dna_len;

        // ANNOTATE_ITERATION_TASK(searchLeading);
        term_dist_lagging += is_complementary_base(dna_data[pos_begin], dna_data[pos_end]) ? 1 : 0;
      }
    }
    // ANNOTATE_SITE_END(lagging_motif_loop);

    return (term_dist_lagging == TERM_STEM_SIZE);
  }
}

}  // namespace aevol
