#include "design_registry.h"
#include "configuration.h"
#include "tools.h"

#include <library/cpp/resource/resource.h>

namespace maps::mrc::browser {

DesignRegistry::DesignRegistry(const std::string& designIconsPath,
               const std::string& designPath)
    : designPath_(designPath)
    , imageStorage_(std::make_shared<renderer::image::ImageStorage>())
{
    auto rendererResources = std::make_shared<std::unordered_map<std::string, std::string>>();

    if (std::filesystem::exists(designIconsPath)
            && std::filesystem::is_directory(designIconsPath)) {
        for (auto& entry : std::filesystem::directory_iterator(designIconsPath)) {
            if (entry.is_regular_file()) {
                rendererResources->emplace(
                    entry.path().filename(), std::filesystem::absolute(entry.path().string()));
            }
        }
    }

    auto loadStyleSetFromResource = [&](const std::string& layer) {
        TString rawJson = NResource::Find(layer + JSON_FILE_EXT);
        renderer::json::Document doc;
        doc.Parse(rawJson.data());

        renderer::style2_renderer::Mode mode = {
            .name = layer,
            .styleSet = std::make_shared<renderer::style2::StyleSet>(
                renderer::style2::StyleSet::fromJson(doc)),
            .resources = rendererResources
        };
        return renderer::design::Design{
            .basicMode = mode,
            .nightMode = mode,
            .imageStorage = imageStorage_
        };
    };

    designsFromResources_ = {
        {LAYER_AGE, loadStyleSetFromResource(LAYER_AGE)},
        {LAYER_AGE_PEDESTRIAN, loadStyleSetFromResource(LAYER_AGE_PEDESTRIAN)},
        {LAYER_AGE_ROAD, loadStyleSetFromResource(LAYER_AGE_ROAD)},
        {LAYER_SIGNAL_SEQUENCES, loadStyleSetFromResource(LAYER_SIGNAL_SEQUENCES)},
        {LAYER_EDGES_COVERAGE, loadStyleSetFromResource(LAYER_EDGES_COVERAGE)},
        {LAYER_PEDESTRIAN_EDGES_NIGHT, loadStyleSetFromResource(LAYER_PEDESTRIAN_EDGES_NIGHT)},
    };

    try {
        reloadDesign();
    } catch (const std::exception& e) {
        ERROR() << "Unable to load designs: " << e.what();
    }
}

std::shared_ptr<renderer::image::ImageStorage> DesignRegistry::imageStorage() const
{
    return imageStorage_;
}

renderer::design::Design DesignRegistry::design(
    const std::string& layer,
    const std::optional<std::string>& designExperimentId) const
{
    {
        std::lock_guard lock{designMutex_};
        const auto designIt = designsFromDataset_.find(layer);

        if (designIt != designsFromDataset_.end()) {
            return designIt->second.design(designExperimentId);
        }
    }

    if (!designsFromResources_.count(layer)) {
        throw yacare::errors::BadRequest() << "invalid layer parameter value";
    }
    return designsFromResources_.at(layer);
}

void DesignRegistry::reloadDesign()
{
    std::unordered_map<std::string, renderer::design::Designs> designs;
    INFO() << "Loading designs from " << designPath_;

    for (const auto& entry : std::filesystem::directory_iterator(designPath_)) {
        if (!entry.is_directory())
            continue;

        const std::string desingName = entry.path().filename();
        designs.emplace(desingName, renderer::design::Designs{entry.path(),
                renderer::design::layerSourceFilter(desingName), imageStorage_});

        INFO() << desingName << " design loaded";
    }

    std::unique_lock lock{designMutex_};
    designsFromDataset_ = std::move(designs);
}

bool DesignRegistry::areCartographDesignsLoaded() const
{
    std::lock_guard lock{designMutex_};
    return !designsFromDataset_.empty();
}

} // namespace maps::mrc::browser
