#include "qs_parsed_entity.h"
#include "qs_component.h"
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/indexer_core/document_parser.h>
#include <saas/library/behaviour/behaviour.h>

void TQSComponentParser::Parse(TParsingContext& context) const {
    bool needInCheckFullFactorsList =
        context.Command != NRTYServer::TMessage::DEPRECATED__UPDATE_DOCUMENT &&
        context.Command != NRTYServer::TMessage::DELETE_DOCUMENT &&
        GetBehaviour(context.Command).IsContentMessage;

    const NRTYFactors::TConfig& factorsConfig = *context.Result.GetConfig().GetSearcherConfig().Factors;
    TQSParsedEntity* result = context.Result.GetComponentEntity<TQSParsedEntity>(QS_COMPONENT_NAME);
    VERIFY_WITH_LOG(result, "there is not Erf entitiy in parsed doc");
    for (ui32 i = 0; i < context.Document.QSInfoSize(); ++i) {
        const NRTYServer::TMessage::TQSInfo& qsInfo = context.Document.GetQSInfo(i);
        const NRTYFactors::TQSFactorsList::TPtr fInfo = factorsConfig.GetQuerySpecFactors(qsInfo.GetName());
        if (!fInfo) {
            ythrow yexception() << "incorrect qs factors group name: " << qsInfo.GetName();
        }
        result->AddQSInfo(qsInfo, fInfo, needInCheckFullFactorsList);
    }
    if (context.Command == NRTYServer::TMessage::DEPRECATED__UPDATE_DOCUMENT && context.Document.QSInfoSize() > 0)
        RestoreIsNeeded(context.Result);
}


TQSParsedEntity::TQSParsedEntity(TConstructParams& params)
    : TParsedDocument::TParsedEntity(params)
{}

void TQSParsedEntity::MergeToProto(NRTYServer::TParsedDoc& pd, const NRTYServer::TDocSerializeContext& context) const {
    TParsedEntity::MergeToProto(pd, context);
    pd.MutableDocument()->ClearQSInfo();
    for (THashMap<TString, TDocumentQSInfo>::const_iterator i = FactorsByQS.begin(), e = FactorsByQS.end(); i != e; ++i) {
        NRTYServer::TMessage::TQSInfo* info = pd.MutableDocument()->AddQSInfo();
        info->SetName(i->first);
        i->second.Serialize(*info);

    }
}

void TQSParsedEntity::AddQSInfo(const NRTYServer::TMessage::TQSInfo& qsInfo, const NRTYFactors::TQSFactorsList::TPtr factorsInfo, bool needInCheckFullFactorsList) {
    VERIFY_WITH_LOG(!FactorsByQS.contains(qsInfo.GetName()), "Incorrect AddQSInfo method usage: %s", qsInfo.GetName().data());
    TDocumentQSInfo& attrs = FactorsByQS[qsInfo.GetName()];
    TVector<ui32> FactorNameToIndex;
    if (needInCheckFullFactorsList && qsInfo.FactorNamesSize() != factorsInfo->GetFactorsList().size())
        throw yexception() << "Incorrect factors count for qs: " << qsInfo.GetName();

    TSet<TString> factorsUsage;
    for (ui32 f = 0; f < qsInfo.FactorNamesSize(); ++f) {
        ui32 factorNum = 0;
        if (!factorsInfo->CheckFactorName(qsInfo.GetFactorNames(f), factorNum)) {
            TString errorMessage = "unknown factor " + qsInfo.GetFactorNames(f) + " for " + qsInfo.GetName();
            ERROR_LOG << errorMessage << Endl;
            throw yexception() << errorMessage;
        }
        factorsUsage.insert(qsInfo.GetFactorNames(f));
        attrs.AddRemapInfo(factorNum);
    }

    if (factorsUsage.size() != qsInfo.FactorNamesSize())
        throw yexception() << "qs factors count for " << qsInfo.GetName() << " is incorrect";

    attrs.SetFactorsInfo(factorsInfo);
    for (ui32 f = 0; f < qsInfo.FactorsSize(); ++f) {
        const NRTYServer::TMessage::TFactorsByKey& value = qsInfo.GetFactors(f);
        if (qsInfo.FactorNamesSize() != value.GetValues().ValuesSize())
            throw yexception() << "Incorrect qsInfo proto: " << qsInfo.GetName();
        attrs.AddInfo(value);
    }

}

void TQSParsedEntity::DoApplyPatch(const TParsedDocument& doc)
{
    const TQSParsedEntity* patchEntity = doc.GetComponentEntity<TQSParsedEntity>(QS_COMPONENT_NAME);
    if (!patchEntity)
        return;
    for (const auto& factors : patchEntity->FactorsByQS)
        FactorsByQS[factors.first].ApplyPatch(factors.second);
}


void TDocumentQSInfo::AddInfo(const NRTYServer::TMessage::TFactorsByKey& info) {
    VERIFY_WITH_LOG(Data.find(QSFactorKeyPrefix + info.GetKey()) == Data.end(), "Incorrect TDocumentQSInfo::AddInfo usage");
    if (info.GetValues().ValuesSize() != Remap.size())
        throw yexception() << "Incorrect factors count for key " << info.GetKey();
    TFactorStorage& values = Data[QSFactorKeyPrefix + info.GetKey()];
    values.SetDomain(TFactorDomain(info.GetValues().ValuesSize()));
    for (ui32 i = 0; i < info.GetValues().ValuesSize(); ++i) {
        float value = info.GetValues().GetValues(i).GetValue();
        if (!FactorsInfo->GetFactorsInfo()->CheckFactorValue(i, value))
            throw yexception() << "Incorrect factor value for key: " << info.GetKey() << ":" << value;
        values[Remap[i]] = value;
    }
}

void TDocumentQSInfo::ApplyPatch(const TDocumentQSInfo& patch) {
    for (const auto& key : patch.Data) {
        auto& data = Data[key.first];
        VERIFY_WITH_LOG(data.Size() == 0 || data.Size() == key.second.Size(), "Invalid data size");
        data.SetDomain(TFactorDomain(key.second.Size()));
        memcpy(data.factors, key.second.factors, sizeof(float) * key.second.Size());
    }
}
