#pragma once

#include <util/generic/vector.h>
#include <util/generic/yexception.h>

struct TCalcFactorsContext;
struct TRTYFunctionCtx;

namespace NRTYFactors {
    // special exception: will switch to factor default value
    class TFactorCalculateDefaultValueException : public yexception {
    };

    class IFactorCalculate {
        [[noreturn]] void NotSupported(TStringBuf s) const {
            ythrow yexception() << s << " is not supported by " << GetCalculatorName();
        }

        mutable bool DependsOnDoc_ = false;

        virtual void UpdateDependsOnDoc() const {
        }

        virtual float DoCalc(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalc");
        }

        virtual i64 DoCalcInt(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalcInt");
        }

        virtual TString DoCalcBlob(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalcBlob");
        }

        virtual TVector<TString> DoCalcBlobVector(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalcBlobVector");
        }

        virtual TVector<float> DoCalcFloatVector(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalcFloatVector");
        }

        virtual TVector<TVector<float> > DoCalcFloatVectors(TCalcFactorsContext& /*ctx*/) const {
            NotSupported("DoCalcFloatVectors");
        }

    public:
        virtual ~IFactorCalculate() {
        }

        virtual TStringBuf GetCalculatorName() const {
            return "unknown_user_function";
        }

        /* If you are about to add new calc type, you should also add it in this class:
        // https://a.yandex-team.ru/arc/trunk/arcadia/saas/rtyserver/factors/user_factors_calcer.cpp?rev=7549672#L1105 */

        float Calc(TCalcFactorsContext& ctx) const {
            auto result = DoCalc(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        i64 CalcInt(TCalcFactorsContext& ctx) const {
            auto result = DoCalcInt(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        TString CalcBlob(TCalcFactorsContext& ctx) const {
            auto result = DoCalcBlob(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        TVector<TString> CalcBlobVector(TCalcFactorsContext& ctx) const {
            auto result = DoCalcBlobVector(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        TVector<float> CalcFloatVector(TCalcFactorsContext& ctx) const {
            auto result = DoCalcFloatVector(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        TVector<TVector<float> > CalcFloatVectors(TCalcFactorsContext& ctx) const {
            auto result = DoCalcFloatVectors(ctx);
            UpdateDependsOnDoc();
            return result;
        }

        virtual bool DependsOnDoc() const {
            return DependsOnDoc_;
        }
        virtual void SetDependsOnDoc() const {
            DependsOnDoc_ = true;
        }
    };
} // namespace NRTYFactors
