#include <string>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include "Utils.h"
using namespace std;

char complement(char b)
{
  switch(b)
    {
    case 'A': return 'T';
    case 'T': return 'A';
    case 'G': return 'C';
    case 'C': return 'G';
      
    case 'a': return 't';
    case 't': return 'a';
    case 'g': return 'c';
    case 'c': return 'g';
    
    case 'N': return 'N';
    case '*': return '*';  
    }
  return '?';
}

string reverse_complement(string seq)
{
  string s(seq.begin(),seq.end());
  string::iterator pos;
  for (pos = s.begin(); pos != s.end(); ++pos) {
    // cout << *pos;
  }
  // cout << endl;
  
  reverse(s.begin(), s.end());
  
  for(pos=s.begin();pos!=s.end();++pos)
    *pos=complement(*pos);
  
  return s;
}

char reverse_dir(char dir)
{
  return dir == 'F' ? 'R' : 'F';
}

int edit_distance(const void *s1, size_t l1, const void *s2, size_t l2, size_t nmemb, int (*comp)(const void*, const void*)) 
{
  unsigned int i;
  unsigned int j;
  size_t len = (l1 + 1) * (l2 + 1);
  char *p1;
  char *p2;
  unsigned int d1;
  unsigned int d2;
  unsigned int d3;
  unsigned int *d;
  unsigned int *dp;
  unsigned int res;
 
  if (l1 == 0)  
    return l2;
  else if (l2 == 0) 
    return l1;
   
  d = (unsigned int*)malloc(len * sizeof(unsigned int));
 
  *d = 0;
  for(i = 1, dp = d + l2 + 1; i < l1 + 1; ++i, dp += l2 + 1)
  {
    *dp = (unsigned) i;
  }
  for(j = 1, dp = d + 1; j < l2 + 1; ++j, ++dp)
  {
    *dp = (unsigned) j;
  }

  for(i = 1, p1 = (char*) s1, dp = d + l2 + 2; i < l1 + 1; ++i, p1 += nmemb, ++dp)
  {
    for(j = 1, p2 = (char*) s2; j < l2 + 1; ++j, p2 += nmemb, ++dp) 
    {
      if(!comp(p1, p2))
      {
        *dp = *(dp - l2 - 2);// same base
      }
      else 
      {
        d1 = *(dp - 1) + 1;// insertion
        d2 = *(dp - l2 - 1) + 1;// deletion
        d3 = *(dp - l2 - 2) + 1;// substitution
        *dp = std::min(d1, std::min(d2, d3));
      }
    }
  }
  res = *(dp - 2);
 
  dp = NULL;
  free(d);
  return res;
}

int hamming_distance(const void *s1, size_t l1, const void *s2, size_t l2, size_t nmemb, int (*comp)(const void*, const void*)) 
{
  char *p1, *p2;
 
  unsigned int i, res = 0;
  for (i = 0, p1 = (char *)s1, p2 = (char *)s2; i < l1; i++, p1 += nmemb, p2 += nmemb)
    if (comp(p1,p2))
      res++;

  return res;
}

int comp(const void *a, const void *b)
{
  return *(char *)a != *(char *)b;
}

FILE* open_file( char* filename )
{
  FILE* file = fopen( filename, "r" );
  
  if ( file == NULL )
  {
    fprintf( stderr, "\nERROR: cannot open file %s\n", filename );
    exit( EXIT_FAILURE );
  }
  
  return file;
}

/*!
 * \brief Modify the sequence to put context in lowercase
 * \param sequence: the sequence to modify
 * \param contextFirst: size of the context on the left
 * \param contextLast: size of the context on the rigth
 * return the modified sequence (with contexts in lower case, and the middle in upper
 * used in BubbleEnumeration & NGraph methods
 */
string toLowerContext( const string &sequence, const int contextFirst, const int contextLast)
{
   string contextL = sequence.substr(0, contextFirst);
   string contextR = sequence.substr( sequence.size() - contextLast, contextLast);
   string varS = sequence.substr(contextFirst, sequence.size() -  (contextFirst + contextLast));
   transform( contextL.begin(), contextL.end(), contextL.begin(), ::tolower);
   transform( contextR.begin(), contextR.end(), contextR.begin(), ::tolower);
   return contextL + varS + contextR;
}

File File::open_path(const char* path, const char* mode) {
    auto ptr = std::unique_ptr<std::FILE, Deleter>{std::fopen(path, mode), Deleter{}};
    if(ptr == nullptr) {
        std::fprintf(stderr, "Error: could not open file %s (mode=%s)\n", path, mode);
        std::exit(EXIT_FAILURE);
    }
    return File{std::move(ptr)};
}
