#pragma once

#include "factors_abstract.h"

#include <saas/util/bit_array_remap.h>

#include <search/reqparam/overridable.h>
#include <kernel/externalrelev/sent_zones.h>

#include <util/generic/string.h>
#include <util/generic/vector.h>
#include <util/string/cast.h>
#include <util/system/types.h>

namespace NRTYFactors {
    extern const size_t NOT_FACTOR;

    struct TFactor;

    class IFactorsInfoWriter {
    public:
        virtual ~IFactorsInfoWriter() {}
        virtual void Write(const TString& sectionName, const TFactor& factor) = 0;
        virtual void Finish() = 0;
        virtual void Clear() = 0;
    };

    struct TSimpleFactorDescription {
        TString Name;
        size_t IndexGlobal; // Index of factor in the final FactorStorage (i.e. an index that is defined in relev.conf)
        ui32 Width;
        TBitsReader::TFieldType Type;
        TOverridable<float> DefaultValue;

    public:
        TSimpleFactorDescription(const TString& name, size_t indexGlobal, ui32 width, TBitsReader::TFieldType type) {
            Name = name;
            IndexGlobal = indexGlobal;
            Width = width;
            Type = type;
        }

        virtual ~TSimpleFactorDescription() = default;

        TString GetHash() const {
            return Name + ToString<ui32>(IndexGlobal);
        }

        bool CheckValue(float value) const {
            if (Width == 32)
                return true;
            else if (value > 1 || value < 0)
                return false;
            return true;
        }
    };

    enum TFactorType { ftStatic, ftCommonStatic, ftDynamic, ftRtyDynamic, ftRtyTimeDynamic, ftUser, ftZone, ftQS, ftSuggest, ftSuggestDict, ftAnn, ftGeoLayer, ftIncorrect, ftMeta };

    struct TIndexWebBase {
        i32 Offset = Max<i32>(); // an offset in the intermediate FactorStorage, which contains multiple slices

    public:
        bool Defined() const {
            return Offset != Max<i32>();
        }
        static constexpr TIndexWebBase Invalid() {
            return TIndexWebBase();
        }
        bool operator==(const TIndexWebBase& other) const {
            return Offset == other.Offset;
        }
        bool operator!=(const TIndexWebBase& other) const {
            return !(*this == other);
        }
    };

    struct TFactor: public TSimpleFactorDescription {
        TFactor(const TSimpleFactorDescription& sfd, TIndexWebBase indexBase, TFactorType factorType);
        TFactor(const TSimpleFactorDescription& sfd, TFactorType factorType);
        TFactor();

    public:
        TString ZoneName;
        NZoneFactors::TZoneFactorType ZoneFactorType;
        TIndexWebBase IndexBase; // index in TFactorStorage(Config.GetDomain())
        TFactorType FactorType;
        EFormClass FormClass;
        bool IsInv;
        TString BaseFactorName;

    private:
        void Init();
        bool ParseZoneFactor(TStringBuf name);
        bool ParseTimeFactor(TStringBuf name);
    };

    template<class TFactorDescr>
    class TFactorsListImpl: public TVector<TFactorDescr>, public IRTYStaticFactors {
    public:
        virtual ui32 GetStaticFactorsCount() const {
            return TVector<TFactorDescr>::size();
        }
        virtual const NRTYFactors::TSimpleFactorDescription& GetFactor(ui32 index) const {
            VERIFY_WITH_LOG(TVector<TFactorDescr>::size() > index, "%lu > %u", TVector<TFactorDescr>::size(), index);
            return (*this)[index];
        }
    };

    typedef TFactorsListImpl<TFactor> TFactorsList;
    typedef TFactorsListImpl<TSimpleFactorDescription> TSimpleFactorsList;

    class TFactorsListWriter: public IFactorsInfoWriter {
    private:
        TFactorsList& List;
    public:
        TFactorsListWriter(TFactorsList& list): List(list) {

        }

        virtual void Finish() {

        }

        void Clear() {
            List.clear();
        }

        void Write(const TString& /*sectionName*/, const TFactor& factor) {
            List.push_back(factor);
        }
    };

    struct TFactorChunk {
        TFactorChunk(ui32 src, ui32 dst)
            : IndexSrc(src)
            , IndexDst(dst)
            , Length(1) {
        }

        ui32 IndexSrc;
        ui32 IndexDst;
        ui32 Length;
    };
    using TFactorChunks = TVector<TFactorChunk>;
    using TUsedFactors = TSet<ui32>;
}
