#pragma once

#include "proto_dump.h"
#include "registry.h"

#include <util/string/hex.h>

namespace NRtDoc {
    class TProtoDumper final: public IProtoDumper {
    private:
        const bool DeepParse;
        const bool UseHex;
        TJsonStringPrintersRegistry Registry;
        TString LastPrepId;
        IProtoJsonPrinter* LastPrinter;

    private:
        static void RegisterOffroad(TJsonStringPrintersRegistry& registry);
        static void RegisterJupiter(TJsonStringPrintersRegistry& registry);

    public:
        TProtoDumper(bool deepParse, bool useHex)
            : DeepParse(deepParse)
            , UseHex(useHex)
        {
            RegisterJupiter(Registry);

            if (DeepParse) {
                RegisterOffroad(Registry);
            }
        }

        virtual bool IsKnown(const TString& tableName) override {
            using TMapType = TJsonStringPrintersRegistry::TPrintersMap;
            const TMapType& lookup = Registry.GetPrinters();

            auto i = lookup.find(tableName);
            if (i == lookup.end()) {
                return false;
            } else {
                LastPrepId = tableName;
                LastPrinter = i->second.Get();
                return true;
            }
        }

        virtual NJson::TJsonValue ToJson(const NJupiter::TMapType& cell) override {
            const TString& tableName = cell.GetName();
            if (tableName != LastPrepId) {
                const bool found = IsKnown(tableName);
                Y_ENSURE(found);
            }

            THolder<NProtoBuf::Message> message;
            NJson::TJsonValue value(NJson::EJsonValueType::JSON_MAP);
            try {
                message = LastPrinter->RawProto2Proto(cell.GetData());
                value = NJson::ReadJsonFastTree(LastPrinter->Proto2Json(*message));
            } catch (yexception& e) {
                value.InsertValue("FAILED to parse", CurrentExceptionMessage());
                if (UseHex) {
                    value.InsertValue("BLOB", HexEncode(cell.GetData()));
                } else {
                    value.InsertValue("BLOB", cell.GetData());
                }
                return value;
            }

            try {
                const auto* deepParser = Registry.GetTransformer(tableName);
                if (deepParser && DeepParse) {
                    deepParser->Transform(value, *message);
                }
            } catch (yexception& e) {
                value.InsertValue("FAILED to DeepParse", CurrentExceptionMessage());
            }

            return value;
        }
    };
}
