#include <DatSubsystems/DatLocaleManager.h>
#include <EasyLogging++/easylogging++.h>
#include <DatFile.h>
#include <SubFile.h>

#define DAT_LOCALE_DICT_VERSION 103

namespace LOTRO_DAT {
    DatLocaleManager::DatLocaleManager(DatFile *datFilePtr) : dat(datFilePtr), current_locale_(ORIGINAL) {
    }

    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Инициализация модуля. Должна происходить после инициализации модуля ввода-вывода и файловой системы.
     * Считывает словарь и проверяет корректность (если структура словаря нарушена и основной является локаль PATHCED,
     * то dat файл считается некорреткным (любое обновлене dat файла с активной локалью PATCHED нарушает структуру файла
     * и делает использование в игре невозможным.
     *
     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Initialise класса DatFile
     *
     * Структура словарей локализации:
     * ======== LOCALE DICT STRUCTURE =========
     * 4                                bytes for dict size (in bytes)
     * 4                                bytes for locale version
     * 4                                bytes for .dat file size (with patches)
     * 4                                bytes for header hash
     * 15                               bytes for "Hi from Gi1dor"
     * 4                                bytes for LOCALE mark ("PATC" or "ORIG")
     * 4                                bytes for orig_dict.size()
     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
     * 4                                bytes for patch_dict.size()
     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
     * 4                                bytes for inactive_categories dict
     * 4 * inactive_categories.size()   bytes for inactive_categories data
     * ========================================
     * Помимо этого:
     * 0x128-0x12C - Флаг, установлена ли альтернативная версия (flag)
     * 0x12C-0x130 - Офсет начала словаря локализации (offset)
     *
     * \warning Если flag != 0 и offset == 0 - то dat файл считается повреждённым. Проверка осуществляется функцией CheckLocaleCorrectж
     */

    DatOperationResult<> DatLocaleManager::Init() {
        if (!dat)
            return DatOperationResult<>(ERROR, "LOCALEINIT: no connection with Dat (dat is nullptr)");

        orig_dict_.clear();
        patch_dict_.clear();
        inactive_categories.clear();
        current_locale_ = ORIGINAL;

        LOG(INFO) << "Initialising locales...";
        BinaryData locale_offset_data(4);
        dat->GetIO().ReadData(locale_offset_data, 4, 300);
        long long locale_offset = locale_offset_data.ToNumber<4>(0);

        if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value) {
            if (CheckLocaleCorrect()) {
                LOG(INFO) << "Dictionary offset is empty or incorrect. Passing.";
                return DatOperationResult<>();
            } else {
                return DatOperationResult<>(ERROR,
                                            "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
            }
        }

        BinaryData locale_info(16);
        dat->GetIO().ReadData(locale_info, 16, locale_offset);

        long long dict_size = locale_info.ToNumber<4>(0);
        long long dict_version = locale_info.ToNumber<4>(4);
        long long patched_file_end = locale_info.ToNumber<4>(8);
        long long dat_file_header_hash = locale_info.ToNumber<4>(12);

        if (dict_version != DAT_LOCALE_DICT_VERSION) {
            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
            if (CheckLocaleCorrect())
                return DatOperationResult<>(SUCCESS);
            else
                return DatOperationResult<>(ERROR,
                                            "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
        }

        if (dat_file_header_hash != dat->GetIO().GetHeaderHash()) {
            LOG(INFO) << "Header hash, written after last patch apply doesn't match current hash. Seems like dat-file was modified somewhere else, so removing all patch data";
            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
            if (CheckLocaleCorrect())
                return DatOperationResult<>(SUCCESS);
            else
                return DatOperationResult<>(ERROR,
                                            "Version of locales' dictionary is incorrect, through patched mark is standing. Dat file may be corrupted");
        }

        BinaryData dicts_data = BinaryData((unsigned) dict_size);
        dat->GetIO().ReadData(dicts_data, dict_size - 16, locale_offset + 16);

        if (dicts_data.size() < 15) {
            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
            return DatOperationResult<>(ERROR, "INITLOCALE: Data in locales' dictionary is incorrect.");
        }

        BinaryData hi_data = dicts_data.CutData(0, 15) + BinaryData("\0", 1);
        std::string hi = std::string((char *) (hi_data.data()));

        if (hi != "Hi from Gi1dor!")
            return DatOperationResult<>(ERROR,
                                        "INITLOCALE: Data in locales' dictionary is incorrect (couldn't receive 'Hello').");

        int offset = 15;
        BinaryData current_locale_data = dicts_data.CutData(offset, offset + 4) + BinaryData("\0", 1);
        std::string locale((char *) (current_locale_data.data()));
        offset += 4;

        if (locale != "PATC" && locale != "ORIG")
            return DatOperationResult<>(ERROR,
                                        "INITLOCALE: Data in locales' dictionary is incorrect (current locale mark is invalid).");

        current_locale_ = (locale == "PATC" ? PATCHED : ORIGINAL);

        size_t orig_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
        offset += 4;
        for (size_t i = 0; i < orig_dict_size; i++) {
            auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
            file.category = dicts_data.ToNumber<4>(offset + 32);
            orig_dict_[file.file_id()] = file;
            offset += 36;
        }

        size_t patch_dict_size = size_t(dicts_data.CutData(offset, offset + 4).ToNumber<4>(0));
        offset += 4;
        for (size_t i = 0; i < patch_dict_size; i++) {
            auto file = SubFile(*dat, dicts_data.CutData(offset, offset + 32));
            file.category = dicts_data.ToNumber<4>(offset + 32);
            patch_dict_[file.file_id()] = file;
            offset += 36;
        }

        size_t inactive_categories_set_size = size_t(dicts_data.ToNumber<4>(offset));
        offset += 4;
        for (size_t i = 0; i < inactive_categories_set_size; i++) {
            size_t category_id = size_t(dicts_data.ToNumber<4>(offset));
            inactive_categories.insert(category_id);
            offset += 4;
        }

        LOG(INFO) << "There are " << patch_dict_.size() << " files in patch locale dictionary";
        LOG(INFO) << "There are " << orig_dict_.size() << " files in original locale dictionary";
        LOG(INFO) << "There are " << inactive_categories.size() << " categories inactive: ";
        LOG(INFO) << "Finished initialising locales";

        if (CheckLocaleCorrect()) {
            dat->GetFileSystem().patched_file_end = patched_file_end;
            LOG(INFO) << "Locales initialisation success. Dictionary size is " << dict_size << ". Version is "
                      << dict_version << ". Localed .dat size = " << patched_file_end;
            return DatOperationResult<>(SUCCESS);
        } else {
            orig_dict_.clear();
            patch_dict_.clear();
            inactive_categories.clear();
            current_locale_ = ORIGINAL;
            return DatOperationResult<>(ERROR,
                                        "Locale dict is incorrect, through patched mark is standing. Dat file may be corrupted");
        }
    }

    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Смена активной локали. Меняет информацию о файлах в файловой системе на соответствующую информацию в локали.
     * \param[in] locale Устанавливаемая локаль
     * \warning Изменения в файловую систему вносятся локально! В dat файл изменения будут записаны во время деинициализации модуля файловой системы
     */

    DatOperationResult<> DatLocaleManager::SetLocale(DatLocaleManager::LOCALE locale) {
        if (dat->GetStatusModule().GetStatus() == DatStatus::E_FREE)
            dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);

        dat->GetStatusModule().SetDebugMessage("Changing locale to " +
                                               (locale == PATCHED ? std::string(" patched version")
                                                                  : std::string(" original version")));

        LOG(INFO) << "Setting locale to " << (locale == PATCHED ? " PATCHED" : " ORIGINAL");
        if (!dat)
            return DatOperationResult<>(ERROR, "SETLOCALE: no connection with Dat (dat is nullptr)");

        if (current_locale_ == locale) {
            LOG(INFO) << "Locale is already " << locale << ", nothing to do.";

            if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
                dat->GetStatusModule().SetDefaultStatus();

            return DatOperationResult<>(SUCCESS);
        }

        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);

        size_t files_total = dict.size();
        size_t files_proceeded = 0;
        dat->GetStatusModule().SetTotalParts(files_total);

        for (const auto &file : dict) {
            ++files_proceeded;
            dat->GetStatusModule().SetFinishedParts(files_proceeded);

            if (CategoryIsInactive(file.second.category) && locale == PATCHED)
                continue;

            long long file_id = file.first;

            auto dict_file_result = dat->GetFileSystem().GetFile(file_id);
            if (dict_file_result.result != SUCCESS) {
                LOG(WARNING) << "Unable to get file with id = " << file_id << "from datFileSystem!";
                dict.erase(file_id);
                continue;
            }

            std::shared_ptr<SubFile> dict_file = dict_file_result.value;

            if (dict_file->MakeHeaderData().CutData(8, 16) == file.second.MakeHeaderData().CutData(8, 16))
                continue;

            dat->GetFileSystem().UpdateFileInfo(file.second);
        }
        current_locale_ = locale;

        if (dat->GetStatusModule().GetStatus() == DatStatus::E_COMMITING)
            dat->GetStatusModule().SetDefaultStatus();

        return DatOperationResult<>(SUCCESS);
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Получение текущей активной локали. Если локали не существуют, понимается, что используется оригинальная (ORIGINAL)
     */

    DatLocaleManager::LOCALE DatLocaleManager::GetCurrentLocale() {
        return current_locale_;
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Деинициализация модуля. Должна происходить перед деинициализацией модулей ввода-вывода и файловой системы.
     */

    DatOperationResult<> DatLocaleManager::DeInit() {
        LOG(INFO) << "Committing locales...";
        if (!dat)
            return DatOperationResult<>(ERROR, "LOCALEDEINIT: no connection with Dat (dat is nullptr)");

        auto result = CommitLocales();
        ClearData();
        return result;
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Обновление данных файла в локали
     * \param[in] locale Локаль, для которой файл данных будет обновлён
     * \param[in] file Новые данные файла. Старые данные будут перезаписаны
     */

    void DatLocaleManager::UpdateLocaleFile(DatLocaleManager::LOCALE locale, const SubFile &file) {
        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
        dict[file.file_id()] = file;
    }

    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Получение данных файла в локали. Вернёт DatOperationResult.result = ERROR, если файла с таким id не существует.
     * \param[in] file_id id файла, для которого необходимо получить данных
     * \param[in] locale Локаль, данные которой будут получены
     */

    DatOperationResult<SubFile> DatLocaleManager::GetLocaleFile(long long file_id, DatLocaleManager::LOCALE locale) {
        std::map<long long, SubFile> &dict = GetLocaleDictReference(locale);
        if (dict.count(file_id) == 0)
            return DatOperationResult<SubFile>(SubFile(), ERROR,
                                               "GETLOCFILE: cannot get file with id = " + std::to_string(file_id) +
                                               " from dict " + std::to_string(locale));
        return DatOperationResult<SubFile>(dict[file_id], SUCCESS);
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Получение словаря файлов локали
     * \param[in] locale Локаль, данные которой будут получены
     */

    std::map<long long, SubFile> &DatLocaleManager::GetLocaleDictReference(DatLocaleManager::LOCALE locale) {
        return locale == PATCHED ? patch_dict_ : orig_dict_;
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Выводит информацию о состоянии модуля в файл
     * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
     * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
     */

    void DatLocaleManager::PrintInformaion(FILE *file) {
        fprintf(file, "========= Locales info ========\n");
        BinaryData locale_offset_data(4);
        dat->GetIO().ReadData(locale_offset_data, 4, 300);
        long long locale_offset = locale_offset_data.ToNumber<4>(0);

        fprintf(file, "Locales' dictionary offset = %lld\n", locale_offset);

        BinaryData locale_status_data(4);
        dat->GetIO().ReadData(locale_status_data, 4, 296);
        long long locale_status = locale_offset_data.ToNumber<4>(0);

        fprintf(file, "Locale status = %lld\n", locale_status);

        if (locale_offset != 0) {
            BinaryData locale_info(16);
            dat->GetIO().ReadData(locale_info, 16, locale_offset);

            long long dict_size = locale_info.ToNumber<4>(0);
            long long dict_version = locale_info.ToNumber<4>(4);

            fprintf(file, "Locales' dictionary size = %lld, version = %lld\n", dict_size, dict_version);
        }

        fprintf(file, "Current locale id = %d\n", current_locale_);
        fprintf(file, "Patch dictionary size = %d\n", patch_dict_.size());
        fprintf(file, "Original dictionary size = %d\n", orig_dict_.size());
    }


    /*!
     * \Author Gi1dor
     * \date 06.07.2018
     * Осуществляет проверку корректности dat файла с позиции локалей
     * Файл считается некорректным, если активной является альтернативная локаль, причём нет возможности вернуть оригинальную (блок локалей неверный/повреждён)
     * Байты 0x128-0x12C равны 0, если на момент пользования патчером активна оригинальная локаль, и DatIO::file_size, если нет
     * Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
     */

    bool DatLocaleManager::CheckLocaleCorrect() {
        BinaryData locale_info(4);
        dat->GetIO().ReadData(locale_info, 4, 296);
        long long locale_status = locale_info.ToNumber<4>(0);

        BinaryData locale_offset_data(4);
        dat->GetIO().ReadData(locale_offset_data, 4, 300);
        long long locale_offset = locale_offset_data.ToNumber<4>(0);

        if (locale_offset == 0 || locale_offset + 8 >= dat->GetIO().GetActualDatSize().value)
            return locale_status == 0;

        BinaryData dicts_data = BinaryData(4);
        auto operation = dat->GetIO().ReadData(dicts_data, 4, locale_offset + 16 + 15);
        if (operation.result == ERROR)
            return locale_status == 0;

        BinaryData locale_data = dicts_data + BinaryData("\0", 1);
        std::string locale((char *) (locale_data.data()));

        LOCALE dat_locale = (locale == "PATC" ? PATCHED : ORIGINAL);

        if ((locale_status == 0 && dat_locale == PATCHED) || (locale_status != 0 && dat_locale == ORIGINAL)) {
            LOG(ERROR) << "CHCKLOCALECORRECT: Locale status and current_locale doesn't match!";
            return false;
        }

        if (locale_status != 0 && locale_status != dat->GetIO().GetHeaderHash()){
            LOG(ERROR) << "CHCKLOCALECORRECT: Locale hash doesn't match!";
            return false;
        }

        return true;
    }


    /*!
     * \Author Gi1dor
     * \date 11.07.2018
     * Проверка активности категории
     * \returns true, если категория активна и false - если нет
     */

    bool DatLocaleManager::CategoryIsInactive(long long category) {
        return inactive_categories.count(category) > 0;
    }


    /*!
    * \Author Gi1dor
    * \date 11.07.2018
    * Обновляет категорию у файлов в словарях локалей
    */

    void DatLocaleManager::UpdateCategory(long long file_id, long long category) {
        if (orig_dict_.count(file_id))
            orig_dict_[file_id].category = category;
        if (patch_dict_.count(file_id))
            patch_dict_[file_id].category = category;
    }

    /*!
    * \Author Gi1dor
    * \date 21.10.2018
    * Возвращает словарь, состоящий из id категорий, помеченных как неактивные
    */

    const std::set<long long> &DatLocaleManager::GetInactiveCategories() {
        return inactive_categories;
    }

    /*!
     * \Author Gi1dor
     * \date 21.10.2018
     * Записывает словарь. Если ранее словарь не существовал - новый будет записан в конец файла.
     * Для словаря выделяется блок не менее 20мб
     *
     * \warning Не должна вызываться вручную! Автоматически вызывается в функции Deinitialise класса DatFile
     *
     * Структура словарей локализации:
     * ======== LOCALE DICT STRUCTURE =========
     * 4                                bytes for dict size (in bytes)
     * 4                                bytes for locale version
     * 4                                bytes for .dat file size (with patches)
     * 4                                bytes for dat file header hash
     * 15                               bytes for "Hi from Gi1dor"
     * 4                                bytes for LOCALE
     * 4                                bytes for orig_dict.size()
     * (32 + 4) * orig_dict.size()      bytes for orig_dict data
     * 4                                bytes for patch_dict.size()
     * (32 + 4) * patch_dict.size()     bytes for patch_dict data
     * 4                                bytes for inactive_categories dict
     * 4 * inactive_categories.size()   bytes for inactive_categories data
     * ========================================
     * Помимо этого:
     * 0x128-0x12C - 0, если выбрана локаль ORIGINAL и обновление клиентом игры не испортит .dat file
     *               хэш хедера файла в противном случае.
     *               Отличие значения в 0x128 от 0 и значения в 0x148 => файл ресурсов мог быть повреждён
     *
     * 0x12C-0x130 - Офсет начала словаря локализации
     */

    DatOperationResult<> DatLocaleManager::CommitLocales() {
        if (!dat->Initialized())
            return DatOperationResult<>(SUCCESS);

        if (patch_dict_.empty()) {
            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
            dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 300);
            return DatOperationResult<>(SUCCESS);
        }

        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
        dat->GetStatusModule().SetTotalParts(orig_dict_.size() + patch_dict_.size());
        dat->GetStatusModule().SetFinishedParts(0);

        BinaryData binary_data = BinaryData(4 + 4 + 4 + 4 + 14 + 15 + 4
                                            + 4 + (32 + 4) * orig_dict_.size()
                                            + 4 + (32 + 4) * patch_dict_.size()
                                            + 4 + 4 * inactive_categories.size());

        // First 16 bytes will be filled just before writing data to file
        size_t current_size = 16;

        binary_data.Append(BinaryData("Hi from Gi1dor!", 15), current_size);
        current_size += 15;

        binary_data.Append(BinaryData((current_locale_ == ORIGINAL ? "ORIG" : "PATC"), 4), current_size);
        current_size += 4;

        binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
        current_size += 4;

        unsigned long long processed_files = 0;

        for (const auto &file : orig_dict_) {
            binary_data.Append(file.second.MakeHeaderData(), current_size);
            current_size += 32;
            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
            current_size += 4;
            dat->GetStatusModule().SetFinishedParts(++processed_files);
        }

        binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
        current_size += 4;

        for (const auto &file : patch_dict_) {
            binary_data.Append(file.second.MakeHeaderData(), current_size);
            current_size += 32;
            binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
            current_size += 4;
            dat->GetStatusModule().SetFinishedParts(++processed_files);
        }

        binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
        current_size += 4;
        for (auto patch_id : inactive_categories) {
            binary_data.Append(BinaryData::FromNumber<4>(patch_id), current_size);
            current_size += 4;
        }


        BinaryData dicts_data(4);
        dat->GetIO().ReadData(dicts_data, 4, 300);
        long long dict_offset = dicts_data.ToNumber<4>(0);
        dat->GetIO().ReadData(dicts_data, 4, dict_offset);
        long long dict_size = dicts_data.ToNumber<4>(0);

        if (binary_data.size() > dict_size || dict_offset == 0) {
            long long new_dict_offset = dat->GetFileSystem().patched_file_end + 16;

            // Updating first 16 bytes
            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
            binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
            binary_data.Append(
                    BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end + binary_data.size() + 20 * 1024 * 1024), 8);
            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);
            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), new_dict_offset);
            if (operation.result != SUCCESS) {
                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales");
            }

            dat->GetIO().WriteData(BinaryData::FromNumber<4>(new_dict_offset), 4, 300);

            if (current_locale_ == PATCHED) {
                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
            } else {
                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
            }
            dat->GetFileSystem().patched_file_end += binary_data.size();

            LOG(INFO) << "Writing 20 mbytes to " << dat->GetFileSystem().patched_file_end;

            BinaryData nulls(unsigned(20 * 1024 * 1024));
            dat->GetIO().WriteData(nulls, nulls.size(), dat->GetFileSystem().patched_file_end);
            dat->GetFileSystem().patched_file_end += nulls.size();
        } else {
            binary_data.Append(BinaryData::FromNumber<4>(std::max(binary_data.size() + 4, 20u * 1024u * 1024u)), 0);
            binary_data.Append(BinaryData::FromNumber<4>(DAT_LOCALE_DICT_VERSION), 4);
            binary_data.Append(BinaryData::FromNumber<4>(dat->GetFileSystem().patched_file_end), 8);
            binary_data.Append(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 12);

            if (current_locale_ == PATCHED) {
                dat->GetIO().WriteData(BinaryData::FromNumber<4>(dat->GetIO().GetHeaderHash()), 4, 296);
            } else {
                dat->GetIO().WriteData(BinaryData::FromNumber<4>(0), 4, 296);
            }

            auto operation = dat->GetIO().WriteData(binary_data, binary_data.size(), dict_offset);

            if (operation.result != SUCCESS) {
                return DatOperationResult<>(ERROR, "LOCALEDEINIT: Cannot write locales. ERRMSG: " + operation.msg);
            }
        }

        dat->GetStatusModule().SetDefaultStatus();
        LOG(INFO) << "Locales committed successfully";
        return DatOperationResult<>(SUCCESS);
    }

    void DatLocaleManager::ClearData() {
        orig_dict_.clear();
        patch_dict_.clear();
        inactive_categories.clear();
        current_locale_ = LOCALE(-1);
    }

    DatOperationResult<> DatLocaleManager::EnableCategory(long long category) {
        inactive_categories.erase(category);
        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
        
        size_t files_count = patch_dict_.size();
        size_t files_processed = 0;
        dat->GetStatusModule().SetTotalParts(files_count);

        for (const auto& entry : patch_dict_) {
            SubFile file = entry.second;
            ++files_processed;
            dat->GetStatusModule().SetFinishedParts(files_processed);

            if (file.category == category) {
                dat->GetFileSystem().UpdateFileInfo(file);
            }
        }

        dat->GetStatusModule().SetDefaultStatus();
        return DatOperationResult<>(SUCCESS);
    }

    DatOperationResult<> DatLocaleManager::DisableCategory(long long category) {
        inactive_categories.insert(category);
        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
        dat->GetStatusModule().SetFinishedParts(0);

        size_t files_count = orig_dict_.size();
        size_t files_processed = 0;
        dat->GetStatusModule().SetTotalParts(files_count);

        for (const auto& entry : orig_dict_) {
            SubFile file = entry.second;
            ++files_processed;
            dat->GetStatusModule().SetFinishedParts(files_processed);

            if (file.category == category) {
                dat->GetFileSystem().UpdateFileInfo(file);
            }
        }

        dat->GetStatusModule().SetDefaultStatus();
        return DatOperationResult<>(SUCCESS);
    }
}
