#ifndef UTILS_H
#define UTILS_H

#include <string>
#include <memory>
#include <vector>

#define MAX 1024
#define NUMBEROFFILES 128
using namespace std;

int edit_distance(const void *s1, size_t l1, const void *s2, size_t l2, size_t nmemb, int (*comp)(const void*, const void*));
int hamming_distance(const void *s1, size_t l1, const void *s2, size_t l2, size_t nmemb, int (*comp)(const void*, const void*));
int comp(const void *a, const void *b); 

char complement(char b);
string reverse_complement(string seq);
char reverse_dir(char dir);

FILE* open_file( char* filename );
string toLowerContext( const string &sequence, const int contextFirst, const int contextLast);

// Wrap std::FILE* in a std::unique_ptr for automatic close on destruction.
struct File {
    struct Deleter {
        void operator()(std::FILE* ptr) { std::fclose(ptr); }
    };
    std::unique_ptr<std::FILE, Deleter> file_ptr;

    // Access raw file pointer
    std::FILE* get_ptr() const { return file_ptr.get(); }
    explicit operator std::FILE*() const { return file_ptr.get(); }

    // Disable default constructor to force use of File::open_*() methods to open a file.
    File() = delete;

    // Open a file at path. Stops program and print error message on failure.
    static File open_path(const char* path, const char* mode);
    static File open_path(const std::string & path, const char* mode) {
        return File::open_path(path.c_str(), mode);
    }

    // Format a path using sprintf then open
    template<typename... Args> static File open_path_sprintf(const char* path_format, const char* mode, Args ...args) {
        char path[1024];
        int written = std::snprintf(path, 1024, path_format, args...);
        if(written < 1024) {
            return File::open_path(path, mode);
        } else {
            auto length = static_cast<std::size_t>(std::snprintf(nullptr, 0, path_format, args...));
            auto buffer = std::vector<char>(length + 1);
            std::snprintf(buffer.data(), buffer.size(), path_format, args...);
            return File::open_path(buffer.data(), mode);
        }
    }

    // Just forward to fprintf
    template<typename... Args> int fprintf(const char* format, Args ...args) const {
        return std::fprintf(this->get_ptr(), format, args...);
    }

    // Force close the file early
    void close() {
        this->file_ptr.reset();
    }
};

#endif
