#pragma once

#include "common.h"
#include "factors_erf.h"

#include <library/cpp/json/json_value.h>
#include <library/cpp/langs/langs.h>

#include <util/generic/hash.h>
#include <util/generic/maybe.h>
#include <util/generic/cast.h>
#include <util/string/cast.h>

namespace NSaas {

    class TAnnBlock {
    public:
        class TRegion {
        public:
            TRegion(NRTYServer::TMessage::TRegionData& proto, ui32 region);

            TRegion& AddStream(const TString& name, const TString& value, bool encode = false);
            NJson::TJsonValue ToJson() const;
            static NJson::TJsonValue ToJson(const NRTYServer::TMessage::TRegionData& proto);

        private:
            NRTYServer::TMessage::TRegionData& InternalProtobuf;
        };

        class TSentence {
        public:
            TSentence(NRTYServer::TMessage::TSentenceData& sent,
                      const TString& text, TMaybe<ELanguage> lang);

            TRegion AddRegion(ui32 region);
            NJson::TJsonValue ToJson() const;
            static NJson::TJsonValue ToJson(const NRTYServer::TMessage::TSentenceData& proto);

        private:
            NRTYServer::TMessage::TSentenceData& InternalProtobuf;
        };

        typedef TAnnBlock TThis;
    public:
        TAnnBlock(NRTYServer::TMessage::TAnnData& annProto);

        NJson::TJsonValue ToJson() const;
        TSentence AddSentence(const TString& text, TMaybe<ELanguage> lang = TMaybe<ELanguage>());

    private:
        NRTYServer::TMessage::TAnnData& InternalProtobuf;
    };

    class TCSBlock {
    public:
        typedef TCSBlock TThis;
    public:
        TCSBlock(NRTYServer::TMessage::TCSInfo& cs)
            : InternalProtobuf(cs)
        {}
        NJson::TJsonValue ToJson() const;

        PROTOBUF_FIELD_ACCESSORS_REF(TString, Name, InternalProtobuf, Name);
        TCSBlock& AddFactor(const TString& name, const TString& value);
        TCSBlock& AddFactor(const TString& name, float value) {
            return AddFactor(name, ToString(value));
        }
    private:
        NRTYServer::TMessage::TCSInfo& InternalProtobuf;
    };

    template<class TValue>
    class TQSBlockImpl {
    public:
        typedef TQSBlockImpl<TValue> TThis;

        class TValueAccessor {
        public:
            TValueAccessor(NRTYServer::TMessage::TFactorValues::TValue& proto)
                : Proto(proto)
            {}

            void Set(TValue value) {
                Proto.SetValue(BitCast<float>(value));
            }
            TValue Get() const {
                return BitCast<TValue>(Proto.GetValue());
            }
        private:
            NRTYServer::TMessage::TFactorValues::TValue& Proto;
        };
    public:
        TQSBlockImpl(NRTYServer::TMessage::TQSInfo& qs)
            : InternalProtobuf(qs)
        {}

        NJson::TJsonValue ToJson() const;

        PROTOBUF_FIELD_ACCESSORS_REF(TString, Name, InternalProtobuf, Name);
        TValueAccessor At(const TString& factor, const TString& key);

    private:
        typedef THashMap<TString, ui64> TPositions;
    private:
        inline ui64 GetPosition(const TString& name, TPositions& positions);
    private:
        static const TString TypeName;
        NRTYServer::TMessage::TQSInfo& InternalProtobuf;
        TPositions KeyPositions;
        TPositions FactorPositions;
    };

    using TQSBlock = TQSBlockImpl<float>;
    using TIntQSBlock = TQSBlockImpl<int>;
}

