#pragma once

#include "factor.h"
#include "factors_mask.h"

#include <kernel/matrixnet/mn_sse.h>
#include <kernel/relevfml/relev_fml.h>

#include <library/cpp/object_factory/object_factory.h>

struct TSumRelevParams;
struct TCalcFactorsContext;

class TRankModel;

namespace NJson {
    class TJsonValue;
}

namespace NRTYFactors {

    class TConfig;

    class IUserRanking {
    public:
        typedef NObjectFactory::TObjectFactory<IUserRanking, TString> TFactory;
        virtual ~IUserRanking() {}
        virtual void InitConfig(const TConfig& relevConf, const NJson::TJsonValue& rankModelConf) = 0;
        virtual bool HasRelevance() const = 0;
        virtual bool HasFactors() const = 0;
        virtual void CalcRelevance(float** factors, float* results, const size_t count) const = 0;
        virtual void CalcFactors(TCalcFactorsContext& ctx) const = 0;
        virtual bool GetUsedFactors(TSet<ui32>& factors) const = 0;
    };

    class TRankModelHolder {
    public:
        struct TOptions {
            bool Normalize = false;
        };

    public:
        TRankModelHolder(const TStringBuf name, const NJson::TJsonValue& config, const TConfig* owner);
        TRankModelHolder(const TStringBuf name, const SRelevanceFormula& polynom, const TConfig* owner, const TRankModelHolder* mxnetSource = nullptr);

        ~TRankModelHolder();

        void MultiCalc(
                    float** factors,
                    float* results,
                    const size_t count,
                    TSumRelevParams* srParamsForCache = nullptr,
                    TMaybe<size_t> modelMatrixNetIndex = Nothing(),
                    TMaybe<size_t> modelPolynomIndex = Nothing()) const;

        const TString& GetName() const {
            return Name;
        }

        const TOptions& GetOptions() const {
            return Options;
        }

        const NRTYFactors::TUsedFactors& GetUsedFactors() const {
            return UsedFactors;
        }

        const TRankModel* GetRankModel() const {
            return Model.Get();
        }

        TAtomicSharedPtr<IUserRanking> GetUserRanking() const {
            return UserRanking;
        }

        TMaybe<size_t> GetMatrixNetIndex() const;

        TMaybe<size_t> GetMetaMatrixNetIndex() const;
        TMaybe<size_t> GetFullMatrixNetIndex() const;
        TMaybe<size_t> GetFastMatrixNetIndex() const;
        TMaybe<size_t> GetFilterMatrixNetIndex() const;
        TMaybe<size_t> GetFastFilterMatrixNetIndex() const;
        TMaybe<size_t> GetMetaPolynomIndex() const;
        TMaybe<size_t> GetFullPolynomIndex() const;
        TMaybe<size_t> GetFastPolynomIndex() const;
        TMaybe<size_t> GetFilterPolynomIndex() const;
        TMaybe<size_t> GetFastFilterPolynomIndex() const;

    private:
        const TString Name;
        const TConfig& FactorsConfig;

        TOptions Options;
        THolder<TRankModel> Model;
        NMatrixnet::TMnSsePtr MxNetInfo;
        THolder<SRelevanceFormula> RelevanceFormula;
        TAtomicSharedPtr<IUserRanking> UserRanking;

        NRTYFactors::TUsedFactors UsedFactors;
        NRTYFactors::TRTYFactorMask RTYFactorMask;
        NRTYFactors::TFactorChunks DynamicFactorsChunks;
    };

    //
    // TFactorSet is like a RankModel, but it calculates nothing - it just affects UsedFactors (used to collect feature pools)
    //
    class TFactorSet {
    public:
        TFactorSet(const TString& name, NRTYFactors::TUsedFactors&& factorIds);
        TFactorSet(const TString& name, const NJson::TJsonValue& config, const TConfig* owner);

        const TString& GetName() const {
            return Name;
        }

        const NRTYFactors::TUsedFactors& GetUsedFactors() const {
            return UsedFactors;
        }

    private:
        const TString Name;
        NRTYFactors::TUsedFactors UsedFactors;
    };
}
