#ifndef FILENAME_UTILITIES_H
#define FILENAME_UTILITIES_H

#include <algorithm>
#include <dirent.h>
#include <sstream>
#include <string>
#include <vector>
#include <math.h>
#include <cmath>

namespace filename
{

static inline
size_t num_fill_zeros(size_t num_elements)
{
    size_t result = (size_t) std::floor(log10(num_elements) + 1);

    return result;
}

static std::string strip_file_extension(std::string filename)
{
    size_t last_dot_pos = filename.rfind(".");

    if (last_dot_pos == std::string::npos)
    {
        return filename;
    }

    return filename.substr(0, last_dot_pos);
}

static std::string get_basename(std::string fullpath)
{
    size_t last_dash_pos = fullpath.rfind("/");

    if (std::string::npos == last_dash_pos)
    {
        return fullpath;
    }

    return fullpath.substr(last_dash_pos + 1);
}


static bool ends_with(std::string filename, std::string suffix)
{
    if(filename.length() < suffix.length())
    {
        return false;
    }

    return 0 == filename.compare(filename.length() - suffix.length(),
                                 suffix.length(),
                                 suffix);
}

static bool starts_with(std::string filename, std::string prefix)
{
    if(filename.length() < prefix.length())
    {
        return false;
    }

    return 0 == filename.compare(0, prefix.length(), prefix);
}

static bool glob_dir(std::string folder, std::string prefix, std::vector<std::string>& matching_files)
{
    DIR* directory;
    directory = opendir(folder.c_str());

    if (!directory)
    {
        printf("Could not open \"%s\" folder: Errno %d\n", folder.c_str(), errno);
        return false;
    }

    dirent* directoryEntry;
    while ((directoryEntry = readdir(directory)) != nullptr)
    {
        if (!(directoryEntry->d_type == DT_REG ||
              directoryEntry->d_type == DT_UNKNOWN))
        {
            continue;
        }

        auto filename = directoryEntry->d_name;
        auto basename = get_basename(filename);

        if (starts_with(basename, prefix))
        {
            matching_files.push_back(folder + filename);
        }
    }

    if (matching_files.size() == 0)
    {
        printf("No files with prefix %s found\n", prefix.c_str());
        return false;
    }

    std::sort(matching_files.begin(), matching_files.end());

    return true;
}

static inline bool glob_cwd(std::string prefix, std::vector<std::string>& matching_files)
{
    return glob_dir("./", prefix, matching_files);
}

//
// Only works on Linux
//
static bool get_all_files_with_ending(std::string suffix,
                                      std::string folder,
                                      std::vector<std::string>& filenames)
{
    std::string prefix(folder);

    const char* folder_c = folder.c_str();

    DIR* directory;
    directory = opendir(folder_c);

    if (!directory) {
        printf("Could not open folder %s: Errno %d\n", folder_c, errno);
        return false;
    }

    dirent* directoryEntry;
    while((directoryEntry = readdir(directory)) != nullptr) {

        auto filename = directoryEntry->d_name;

        if (ends_with(filename, suffix))
        {
            filenames.push_back(prefix + std::string(filename));
        }
    }

    if (filenames.size() == 0)
    {
        printf("No \"%s\" files found in %s\n", suffix.c_str(), folder.c_str());
        return false;
    }

    std::sort(filenames.begin(), filenames.end());

    return true;
}

static bool get_jpg_image_files_in_folder(std::string folder, std::vector<std::string>& jpgFiles)
{
    return get_all_files_with_ending(".jpg", folder, jpgFiles);
}


static bool includes_file_ending(std::string filename)
{
    size_t last_dash_pos = filename.rfind("/");
    size_t last_dot_pos  = filename.rfind(".");

    bool dash_exists = std::string::npos != last_dash_pos;
    if (dash_exists)
    {
        // Check if the dot is located after the last dash
        bool dot_exists     = std::string::npos != last_dot_pos;
        bool dot_after_dash = last_dash_pos < last_dot_pos;

        return dot_exists && dot_after_dash;
    }
    else
    {
        // No dash in filename, check if there is a dot
        bool dot_exists = std::string::npos != last_dot_pos;

        return dot_exists;
    }
}


static inline void maybe_append_format(std::stringstream& ss,
                                       const std::string& current_filename,
                                       const std::string& file_format_string)
{
    // If current_filename does not already include a file format
    if (!includes_file_ending(current_filename))
    {
        ss << file_format_string;
    }
}

static inline std::string maybe_append_format(const std::string& current_filename,
                                              const std::string& file_format_string)
{
    // If current_filename does not already include a file format
    if (!includes_file_ending(current_filename))
    {
        return file_format_string;
    }

    return "";
}

} // namespace filename
#endif // FILENAME_UTILITIES_H
