#ifndef LOTRO_DAT_LIBRARY_DATSTATUS_H
#define LOTRO_DAT_LIBRARY_DATSTATUS_H

#include <string>
#include <functional>
#include <set>

namespace LOTRO_DAT {
    class DatFile;

    /*!
     * \brief Модуль статуса dat-файла
     * \author Gi1dor
     * \date 06.07.2018
     *
     * Класс для хранения информации о выполняемых процессах в dat-файле. Позволяет отслеживать прогресс выполнения
     * во время операций создания резервных копий, применения патчей или извлечения файлов
     *
     * \warning Объекты этого класса не должны создаваться отдельно! Созданием и управлением ими занимается класс DatFile
     */

    class DatStatus {
    public:
        enum DAT_STATUS: int {
            E_INITIALISING,
            E_EXTRACTING,
            E_PATCHING,
            E_COMMITING,
            E_BACKUP_CREATING,
            E_BACKUP_RESTORING,
            E_BACKUP_REMOVING,
            E_GATHERING_INFO,
            E_FREE
        };

        struct ProgressInfo {
            DAT_STATUS status;
            double percentage;
            unsigned long long finished_parts;
            unsigned long long total_parts;
        };

        using Callback = std::function<void(ProgressInfo)>; 
        
        struct CallbackStorage{
        public:
            CallbackStorage(Callback function_handle) {
                func_ = function_handle;
                handler_ = free_handler_++;
            }

            CallbackStorage(unsigned long long handler) {
                func_ = nullptr;
                handler_ = handler;
            }

            unsigned long long GetHandler() const {
                return handler_;
            }

            bool operator<(const CallbackStorage& other) const {
                return handler_ < other.handler_;
            }

            bool operator==(const CallbackStorage& other) const {
                return handler_ == other.handler_;
            }
            
            template<class ... Types>
            void operator()(Types ... args) const {
                func_(args...);
            }

        private:
            Callback func_;
            unsigned long long handler_;
            static unsigned long long free_handler_;
        };
        
        DatStatus() = delete;

        DatStatus(const DatStatus &other) = delete;

        DatStatus &operator=(const DatStatus &other) = delete;

        ~DatStatus() = default;

        explicit DatStatus(DatFile *datFilePtr);

        void SetStatus(DAT_STATUS status);

        void SetFinishedParts(unsigned long long finished_parts);

        void SetTotalParts(unsigned long long total_parts);

        void SetDebugMessage(const std::string &message);

        DAT_STATUS GetStatus();

        double GetPercentage();

        unsigned long long GetFinishedParts();

        unsigned long long GetTotalParts(); 

        std::string GetDebugMessage();

        void SetDefaultStatus();

        bool CheckIfNotPatched();
        
        unsigned long long AddStatusChangedCallbackFunction(Callback func);

        void RemoveStatusChangedCallbackFunction(unsigned long long handler);

        void EraseAllCallbackFunctions();

        void InvokeCallbackFunctions();

    private:
        DatFile *dat = nullptr;
        DAT_STATUS status_ = DAT_STATUS::E_FREE;
        unsigned long long finished_parts_ = 0;
        unsigned long long total_parts_ = 0;
        double percentage_ = 0;
        std::string debug_message_ = "";

        std::set<CallbackStorage> callback_functions_;
    };
}

#endif //LOTRO_DAT_LIBRARY_DATSTATUS_H
