#pragma once

#include <library/cpp/json/json_reader.h>

#include <robot/jupiter/library/data_printer/json_printer.h>

namespace NRtDoc {
    // This is adapted from robot/jupiter/tools/difftool/lib/diff_printer.cpp

    class IProtoJsonPrinter : public TSimpleRefCount<IProtoJsonPrinter> {
    public:
        virtual ~IProtoJsonPrinter() {
        }

        virtual THolder<NProtoBuf::Message> RawProto2Proto(const TString& rawProto) const = 0;

        virtual TString Proto2Json(const NProtoBuf::Message& message) {
            return NJupiter::JupiterProtoToJson(message);
        }
    };

    template<class TProto>
    class TJsonPrinter : public IProtoJsonPrinter {
    public:
        virtual THolder<NProtoBuf::Message> RawProto2Proto(const TString& rawProto) const override {
            auto message = MakeHolder<TProto>();
            Y_PROTOBUF_SUPPRESS_NODISCARD message->ParseFromString(rawProto);
            return message;
        }
    };

    class IProtoJsonTransformer : public TSimpleRefCount<IProtoJsonTransformer> {
    public:
        virtual ~IProtoJsonTransformer() = default;

        virtual bool Transform(NJson::TJsonValue& json, const NProtoBuf::Message& msg) const = 0;
    };

    class TJsonStringPrintersRegistry {
    public:
        using TPrintersMap = THashMap<TString, TIntrusivePtr<IProtoJsonPrinter>>;
        using TTransformersMap = THashMap<TString, TIntrusivePtr<IProtoJsonTransformer>>;

    public:
        TJsonStringPrintersRegistry() {
        }

        template <class TProto>
        inline void RegisterTable(const NJupiter::TTable<TProto>& table) {
            TString prepId = NJupiter::GetYtPathName(table);
            Printers[prepId] = new TJsonPrinter<TProto>();
        }

        template <class TProto>
        inline void RegisterFormat(const NJupiter::TTable<TProto>& table, TIntrusivePtr<IProtoJsonTransformer> handler) {
            TString prepId = NJupiter::GetYtPathName(table);
            Transformers[prepId] = handler;
        }
        /*template <class TIo, class TProto>
        inline void RegisterOffroad(const NJupiter::TTable<TProto>& table) {
            TString prepId = NJupiter::GetYtPathName(table);
            Transformers[prepId] = new TOffroadJsonPrinter<TProto, TIo>();
        }*/

        inline const TPrintersMap& GetPrinters() const {
            return Printers;
        }

        inline const TTransformersMap& GetTransformers() const {
            return Transformers;
        }

        inline const IProtoJsonTransformer* GetTransformer(const TString& prepId) const {
            auto i = Transformers.find(prepId);
            if (i == Transformers.end())
                return nullptr;
            return i->second.Get();
        }

    private:
        TPrintersMap Printers;
        TTransformersMap Transformers;
    };
}
