#include "factor.h"
#include <kernel/web_factors_info/factor_names.h>

namespace NRTYFactors {
    struct TTypeBind {
        TString Prefix;
        NZoneFactors::TZoneFactorType Type;
        bool SelectFormClass;
    };

    struct TFormBind {
        TString Prefix;
        EFormClass FormClass;
    };

    const TTypeBind ZoneFactorTypes[] = {
        { "_BM25F_",    NZoneFactors::zftBM25,  true },
        { "_CZL_",      NZoneFactors::zftCZL,   true },
        { "_CZ_",       NZoneFactors::zftCZ,    true },
        { "_ZL_",       NZoneFactors::zftZL,    false },
        { "_CM_",       NZoneFactors::zftCM,    true },
        { "_IF_",       NZoneFactors::zftIF,    true },
    };

    const TFormBind FormClassTypes[] = {
        { "Sy_", EQUAL_BY_SYNONYM },
        { "Lm_", EQUAL_BY_LEMMA },
        { "St_", EQUAL_BY_STRING }
    };

    const size_t NOT_FACTOR = Max();

    TFactor::TFactor()
        : TSimpleFactorDescription("", NOT_FACTOR, 0, TBitsReader::ftInt)
        , ZoneFactorType(NZoneFactors::zftNoZone)
        , FactorType(ftIncorrect) {
    }

    TFactor::TFactor(const TSimpleFactorDescription& sfd, TIndexWebBase indexBase, TFactorType factorType)
        : TSimpleFactorDescription(sfd)
        , ZoneFactorType(NZoneFactors::zftNoZone)
        , IndexBase(indexBase)
        , FactorType(factorType)
        , FormClass(EQUAL_BY_STRING)
    {
        Init();
    }

    TFactor::TFactor(const TSimpleFactorDescription& sfd, TFactorType factorType)
        : TSimpleFactorDescription(sfd)
        , ZoneFactorType(NZoneFactors::zftNoZone)
        , IndexBase(TIndexWebBase::Invalid())
        , FactorType(factorType)
        , FormClass(EQUAL_BY_STRING)
    {
        Init();
    }

    void TFactor::Init() {
        if (ParseTimeFactor(Name))
            return;
        if (ParseZoneFactor(Name))
            return;
    }

    bool TFactor::ParseZoneFactor(TStringBuf name) {
        for (auto&& type: ZoneFactorTypes) {
            if (!name.StartsWith(type.Prefix))
                continue;

            ZoneFactorType = type.Type;
            name = name.Tail(type.Prefix.size());

            if (type.SelectFormClass) {
                bool detected = false;
                for (auto&& form : FormClassTypes) {
                    if (!name.StartsWith(form.Prefix))
                        continue;

                    FormClass = form.FormClass;
                    name = name.Tail(form.Prefix.size());
                    detected = true;
                    break;
                }

                if (!detected)
                    ythrow yexception() << "cannot detect FormClass form string " << name;
            }

            ZoneName = name;
            return true;
        }
        return false;
    }

    bool TFactor::ParseTimeFactor(TStringBuf name) {
        if (name.StartsWith("_Time_")) {
            TStringBuf descr = name.substr(6);
            if (descr.StartsWith("Inv_")) {
                IsInv = true;
                BaseFactorName = descr.substr(4);
            } else if (descr.StartsWith('_')) {
                IsInv = false;
                BaseFactorName = descr.substr(1);
            } else
                ythrow yexception() << "Incorrect Time factors description: " << name;
            FactorType = ftRtyTimeDynamic;
            return true;
        }
        return false;
    }
}
