#ifndef PHOTOCOMPRESSARCHIVER_H
#define PHOTOCOMPRESSARCHIVER_H

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include <boost/filesystem.hpp>
#include <boost/regex.hpp>

#include "ProtectedAndSynchronizedQueue.h"

// Alias the boost fs namespace to keep the code readable
namespace bfs = boost::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.4.1");

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

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

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

class WorkUnit
{
    public:
        bfs::path m_path;
        uint32_t m_file_size;

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

class PhotoCompressArchiver
{
    public:
    
        PhotoCompressArchiver(
                const std::string& path, 
                uint32_t num_threads,
                bool decompress,
                bool delete_orig,
                bool verbose, 
                boost::regex& filter_regex);
        
        ~PhotoCompressArchiver();
        
        /**
         * Start processing
         */ 
        int execute();
        
        /**
         * Recursively find input files based on passed regex
         */ 
        void find_files(
            const bfs::path& dir_path, 
            const boost::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 bfs::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)
        ProtectedAndSynchronizedQueue<WorkUnit*> m_file_list;
        
        // list of any errors
        ProtectedAndSynchronizedQueue<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
        boost::regex& m_filter_regex;           // filter regular expression
};

#endif // PHOTOCOMPRESSARCHIVER_H
