#ifndef PHOTOCOMPRESSARCHIVER_H
#define PHOTOCOMPRESSARCHIVER_H

#include <atomic>
#include <cstdint>
#include <filesystem>
#include <mutex>
#include <regex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include "ProtectedQueue.h"

// Alias the namespace to keep the code readable
namespace sfs = std::filesystem;

const std::string BOLD("\033[1m");
const std::string ENDC("\033[0m");
const std::string RED("\033[31m");
const std::string YELLOW("\033[33m");
const std::string PURPLE("\033[35m");
const std::string UP_ONE = "\033[1A";
const std::string DOWN_ONE = "\033[1B";

const std::string version("v0.5.1");

const std::regex JPEG_REGEX("^.+\\.((jpg)|(jpeg))$", std::regex_constants::ECMAScript | std::regex::icase);
const std::regex PJG_REGEX("^.+\\.pjg$",  std::regex_constants::ECMAScript | std::regex::icase);

const std::string JPG_EXTENSION(".jpg");
const std::string PJG_EXTENSION(".pjg");

constexpr uint32_t PROGRESS_BAR_WIDTH(80);
const std::string LINE(100, '-');

class WorkUnit
{
public:

    sfs::path m_path;
    uint32_t m_file_size;

    explicit WorkUnit(const sfs::path& path)
            : m_path(path),
              m_file_size(0)
    {
        // Automagically determine the file size
        m_file_size = sfs::file_size(path);
    };
};

class PhotoCompressArchiver
{
public:

    PhotoCompressArchiver(
        const std::string& path,
        uint32_t num_threads,
        bool decompress,
        bool delete_orig,
        bool verbose,
        bool filter_empty,
        std::regex& filter_regex);

    ~PhotoCompressArchiver();

    /**
     * Start processing
     */
    int execute();

    /**
     * Recursively find input files based on passed regex
     */
    void find_files(
        const sfs::path& dir_path,
        const std::regex& file_regex);

    /**
     * Compress/decompress worker thread body
     */
    void worker_thread_body(uint32_t tid);

    /**
     * Calculate the return the "global" percent done as determined by the
     * m_files_processed and m_num_input_files instance variables.
     */
    double get_global_percent_done();

    /**
     * Asynchronously terminate processing.
     */
    void terminate();

private:

    const sfs::path m_path;             // top level path where to look for input files
    uint32_t m_num_threads;             // number of processing threads to use
    bool m_decompress;                  // decompress files instead of compress them
    bool m_delete_orig;                 // delete original files after de/compression
    bool m_verbose;                     // verbose processing output

    std::mutex m_output_mutex;          // output mutex

    bool m_terminate_flag;              // terminate flag

    std::atomic<uint32_t> m_files_processed;    // number of input files processed
    uint32_t m_num_input_files;                 // total number of input files found
    uint64_t m_total_uncompressed_size;         // total uncompressed size of all images
    uint64_t m_total_compressed_size;           // total compressed size of all images
    uint64_t m_running_compressed_size;         // running compressed size (for status)

    // input file list (either images or compressed images)
    ProtectedQueue<WorkUnit*> m_file_list;

    // list of any errors
    ProtectedQueue<WorkUnit*> m_errors;

    std::thread* p_finder_thread;                   // file finder thread
    std::vector<std::thread*> m_worker_threads;     // worker threads

    bool m_filter_empty;                // is the filter regex empty
    std::regex& m_filter_regex;         // filter regular expression
};

#endif // PHOTOCOMPRESSARCHIVER_H
