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

#include "Codon.h"

namespace aevol {

#ifdef BASE_4
bool TranslationInitiationSequence::is_translation_initiation_sequence(
    const Dna& dna, Dna::size_type pos, Strand strand) {
  if (not is_shine_dalgarno(dna, pos, strand)) {
    return false;
  }

  auto start_codon_pos = (strand == Strand::LEADING) ? pos + SHINE_DAL_SIZE + SHINE_START_SPACER
                                                     : pos - SHINE_DAL_SIZE - SHINE_START_SPACER;

  if (not is_start_codon(dna, start_codon_pos, strand)) {
    return false;
  }

  return true;
}

bool TranslationInitiationSequence::is_shine_dalgarno(const Dna& dna, Dna::size_type pos, Strand strand) {
  for (Dna::size_type k = 0; k < SHINE_DAL_SIZE; ++k) {
    if (strand == Strand::LEADING) {
      if (dna.get_lead(pos + k) != SHINE_DAL_SEQ[k]) {
        return false;
      }
    } else {
      if (dna.get_lag(pos - k) != SHINE_DAL_SEQ[k]) {
        return false;
      }
    }
  }

  return true;
}

bool TranslationInitiationSequence::is_start_codon(const Dna& dna, Dna::size_type pos, Strand strand) {
  if (strand == Strand::LEADING) {
    return aevol::is_start_codon(bases_to_codon_value(dna.get_lead(pos), dna.get_lead(pos + 1), dna.get_lead(pos + 2)));
  } else {
    return aevol::is_start_codon(bases_to_codon_value(dna.get_lag(pos), dna.get_lag(pos - 1), dna.get_lag(pos - 2)));
  }
}

#elif BASE_2

bool TranslationInitiationSequence::is_translation_initiation_sequence(
    const Dna& dna, Dna::size_type pos, Strand strand) {
  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

  #ifdef __VECTORIZE_STRCMP
  bool b_start   = false;
  bool start[14] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false};
  #else //  __VECTORIZE_STRCMP
  #if defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
  __declspec(align(64)) bool start[14] =
      {false, false, false, false, false, false, false, false, false, false, false, false, false, false};
  #else // defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
  bool start[14] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false};
  #endif // defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
  #endif //  __VECTORIZE_STRCMP

  if (strand == Strand::LEADING) {
    // Search for Shine Dalgarro + START codon on LEADING
    if (pos + 15 < dna.length() && pos + 15 >= 0) {
      #ifdef __VECTORIZE_STRCMP
      bool compare_1 = strncmp(&(dna_data[c_pos]), SHINE_DAL_SEQ_LEAD, 6) == 0 ? true : false;
      bool compare_2 = strncmp(&(dna_data[c_pos + 10]), &(SHINE_DAL_SEQ_LEAD[10]), 3) == 0 ? true : false;
      b_start        = compare_1 && compare_2;
      #else //  __VECTORIZE_STRCMP
      #if defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
      #pragma omp simd aligned(dna_data : 64) aligned(SHINE_DAL_SEQ_LEAD : 64) aligned(start : 64)
      #else // defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
      #pragma omp simd
      #endif // defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
      for (int32_t k = 0; k < 12; k++) {
        start[k] = dna_data[pos + k] == SHINE_DAL_SEQ_LEAD[k];
      }
      start[12] = dna_data[pos + 12] == SHINE_DAL_SEQ_LEAD[12];
      #endif //  __VECTORIZE_STRCMP
    }
    else {
      for (int32_t k = 0; k < 9; k++) {
        int32_t k_t   = k >= 6 ? k + 4 : k;
        int32_t pos_m = pos + k_t;

        while (pos_m < 0)
          pos_m += dna.length();
        while (pos_m >= dna.length())
          pos_m -= dna.length();

        start[k_t] = (dna_data[pos_m] == SHINE_DAL_SEQ_LEAD[k_t]) ? true : false;
      }
      #ifdef __VECTORIZE_STRCMP
      b_start =
          start[0] && start[1] && start[2] && start[3] && start[4] && start[5] && start[10] && start[11] && start[12];
  #endif // __VECTORIZE_STRCMP
    }
  } else {
    // Search for Shine Dalgarro + START codon on LAGGING
    if (pos - 15 < dna.length() && pos - 15 >= 0) {
      #ifdef __VECTORIZE_STRCMP
      const char buffer_str[13];

      for (int i = 0; i < 13; i++) {
        buffer_str[i] = dna_data[c_pos - i];
      }
      bool compare_1 = strncmp(buffer_str, SHINE_DAL_SEQ_LAG, 6) == 0 ? true : false;
      bool compare_2 = strncmp(&(buffer_str[10]), &(SHINE_DAL_SEQ_LAG[10]), 3) == 0 ? true : false;

      b_start = compare_1 && compare_2;
      #else // __VECTORIZE_STRCMP
      #pragma omp simd
      for (int32_t k = 0; k < 12; k++) {
        start[k] = dna_data[pos - k] == SHINE_DAL_SEQ_LAG[k];
      }
      start[12] = dna_data[pos - 12] == SHINE_DAL_SEQ_LAG[12];
      #endif // __VECTORIZE_STRCMP
    } else {
      for (int32_t k = 0; k < 9; k++) {
        int32_t k_t   = k >= 6 ? k + 4 : k;
        int32_t pos_m = pos - k_t;

        while (pos_m < 0)
          pos_m += dna.length();
        while (pos_m >= dna.length())
          pos_m -= dna.length();

        start[k_t] = (dna_data[pos_m] == SHINE_DAL_SEQ_LAG[k_t]) ? true : false;
      }
      #ifdef __VECTORIZE_STRCMP
      b_start = start[0] && start[1] && start[2] && start[3] && start[4] && start[5] && start[10] &&
                start[11] && start[12];
      #endif // __VECTORIZE_STRCMP
    }
  }

  #ifdef __VECTORIZE_STRCMP
  return b_start;
  #else // __VECTORIZE_STRCMP
  return (start[0] && start[1] && start[2] && start[3] && start[4] && start[5] && start[10] && start[11] && start[12]);
  #endif // __VECTORIZE_STRCMP
}
#endif

}  // namespace aevol
