#include "complex_factors.h"
#include "factors_erf.h"
#include "lables.h"

using namespace NSaas;

TAnnBlock::TRegion::TRegion(NRTYServer::TMessage::TRegionData& proto, ui32 region)
    : InternalProtobuf(proto)
{
    InternalProtobuf.SetRegion(region);
}

TAnnBlock::TRegion& TAnnBlock::TRegion::AddStream(const TString& name, const TString& value, bool encode) {
    NRTYServer::TMessage::TStreamData* stream = InternalProtobuf.AddStreams();
    NSaas::AddSimpleStream(name, value, *stream, encode);
    return *this;
}

NJson::TJsonValue TAnnBlock::TRegion::ToJson() const {
    return ToJson(InternalProtobuf);
}

NJson::TJsonValue TAnnBlock::TRegion::ToJson(const NRTYServer::TMessage::TRegionData& proto) {
    NJson::TJsonValue result(NJson::JSON_MAP);
    result[NJsonFormat::AnnSentenceRegionLable] = proto.GetRegion();

    NJson::TJsonValue streams(NJson::JSON_MAP);
    for (ui64 i = 0; i < proto.StreamsSize(); ++i) {
        streams.InsertValue(proto.GetStreams(i).GetName(), proto.GetStreams(i).GetValue().GetValue());
    }
    result[NJsonFormat::AnnStreamsLable] = streams;
    return result;
}

TAnnBlock::TSentence::TSentence(NRTYServer::TMessage::TSentenceData& sent,
        const TString& text, TMaybe<ELanguage> lang)
    : InternalProtobuf(sent)
{
    InternalProtobuf.SetText(text);
    if (lang.Defined())
        InternalProtobuf.SetTextLanguage(*lang.Get());
}

TAnnBlock::TRegion TAnnBlock::TSentence::AddRegion(ui32 region) {
    return TRegion(*InternalProtobuf.AddStreamsByRegion(), region);
}

NJson::TJsonValue TAnnBlock::TSentence::ToJson() const {
    return ToJson(InternalProtobuf);
}

NJson::TJsonValue TAnnBlock::TSentence::ToJson(const NRTYServer::TMessage::TSentenceData& proto) {
    NJson::TJsonValue result(NJson::JSON_MAP);
    result[NJsonFormat::AnnSentenceLable] = proto.GetText();
    if (proto.HasTextLanguage())
        result[NJsonFormat::AnnSentenceLangLable] = proto.GetTextLanguage();

    NJson::TJsonValue regions(NJson::JSON_ARRAY);
    for (ui64 i = 0; i < proto.StreamsByRegionSize(); ++i) {
        regions.AppendValue(TRegion::ToJson(proto.GetStreamsByRegion(i)));
    }

    result[NJsonFormat::AnnSentenceRegionsArrayLable] = regions;
    return result;
}

TAnnBlock::TAnnBlock(NRTYServer::TMessage::TAnnData& annProto)
    : InternalProtobuf(annProto)
{}

NJson::TJsonValue TAnnBlock::ToJson() const {
    NJson::TJsonValue result(NJson::JSON_MAP);
    NJson::TJsonValue sentences(NJson::JSON_ARRAY);

    for (ui64 i = 0; i < InternalProtobuf.SentencesSize(); ++i) {
        sentences.AppendValue(TSentence::ToJson(InternalProtobuf.GetSentences(i)));
    }

    result.InsertValue(NJsonFormat::TypeLable, "#a");
    result.InsertValue(NJsonFormat::ValueLable, sentences);
    return result;
}

TAnnBlock::TSentence TAnnBlock::AddSentence(const TString& text, TMaybe<ELanguage> lang) {
    return TSentence(*InternalProtobuf.AddSentences(), text, lang);
}

TCSBlock& TCSBlock::AddFactor(const TString& name, const TString& value) {
    NSaas::AddSimpleFactor(name, value, * InternalProtobuf.MutableFactors());
    return *this;
}

NJson::TJsonValue TCSBlock::ToJson() const {
    NJson::TJsonValue result(NJson::JSON_MAP);

    NJson::TJsonValue csMap(NJson::JSON_MAP);
    for (ui64 i = 0; i < InternalProtobuf.GetFactors().NamesSize(); ++i){
        csMap.InsertValue(InternalProtobuf.GetFactors().GetNames(i), InternalProtobuf.GetFactors().GetValues().GetValues(i).GetValue());
    }

    result.InsertValue(NJsonFormat::TypeLable, "#c");
    result.InsertValue(NJsonFormat::ValueLable, csMap);
    return result;
}

template<class TValue>
typename TQSBlockImpl<TValue>::TValueAccessor TQSBlockImpl<TValue>::At(const TString& factor, const TString& key) {
    const ui64 kpos = GetPosition(key, KeyPositions);
    const ui64 fpos = GetPosition(factor, FactorPositions);
    for (ui64 i = InternalProtobuf.FactorsSize(); i <= kpos; ++i)
        InternalProtobuf.AddFactors();

    for (ui64 i = InternalProtobuf.FactorNamesSize(); i <= fpos; ++i)
        InternalProtobuf.AddFactorNames();

    for (ui64 i = InternalProtobuf.GetFactors(kpos).GetValues().ValuesSize(); i <= fpos; ++i)
        InternalProtobuf.MutableFactors(kpos)->MutableValues()->AddValues()->SetValue(0);

    InternalProtobuf.SetFactorNames(fpos, factor);
    InternalProtobuf.MutableFactors(kpos)->SetKey(key);

    return TValueAccessor(*InternalProtobuf.MutableFactors(kpos)->
                           MutableValues()->MutableValues(fpos));
}

template<class TValue>
ui64 TQSBlockImpl<TValue>::GetPosition(const TString& name, TPositions& positions) {
    TPositions::iterator i = positions.find(name);
    if (i == positions.end()) {
        ui64 index = positions.size();
        i = positions.insert(TPositions::value_type(name, index)).first;
    }
    return i->second;
}

template<class TValue>
NJson::TJsonValue TQSBlockImpl<TValue>::ToJson() const {
    NJson::TJsonValue result;

    NJson::TJsonValue qsMap(NJson::JSON_MAP);
    NJson::TJsonValue& qsFactList = qsMap.InsertValue("factor_names", NJson::JSON_ARRAY);
    for (int i = 0; i < InternalProtobuf.GetFactorNames().size(); ++i) {
        qsFactList.AppendValue(InternalProtobuf.GetFactorNames(i));
    }
    NJson::TJsonValue& qsFactMap = qsMap.InsertValue("factors", NJson::JSON_MAP);
    for (int i = 0; i < InternalProtobuf.GetFactors().size(); ++i) {
        NJson::TJsonValue& oneKeyArr = qsFactMap.InsertValue(InternalProtobuf.GetFactors(i).GetKey(), NJson::JSON_ARRAY);
        for (ui64 j = 0; j < InternalProtobuf.GetFactors(i).GetValues().ValuesSize(); ++j) {
            oneKeyArr.AppendValue(BitCast<TValue>(InternalProtobuf.GetFactors(i).GetValues().GetValues(j).GetValue()));
        }
    }

    result.InsertValue(NJsonFormat::TypeLable, TypeName);
    result.InsertValue(NJsonFormat::ValueLable, qsMap);
    return result;
}

template<> const TString TQSBlockImpl<float>::TypeName = "#q";
template<> const TString TQSBlockImpl<int>::TypeName = "#Q";

namespace NSaas {
    template class TQSBlockImpl<float>;
    template class TQSBlockImpl<int>;
}
