#include "fml_ops_script.h"
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <kernel/relevfml/relev_fml.h>
#include <kernel/matrixnet/mn_dynamic.h>
#include <util/stream/format.h>
#include <util/string/strip.h>
#include <util/string/split.h>

namespace NRTYDeploy {

    namespace {
        const TString F_FACTOR_PREFIX = "fFactor[";
    }

    bool TScriptFmlOps::Process(IDeployInfoRequest& request) {
        const TCgiParameters& cgi = request.GetRD().CgiParam;
        const TString& action = cgi.Get("action");
        RelevHelper.LoadFactors(request, cgi.Get("service"), cgi.Get("ctype"));
        TString polynom = StripString(cgi.Get("polynom"));
        if (!polynom)
            polynom = TString(request.GetBlob().AsCharPtr(), request.GetBlob().Size());
        TString result;
        if (action == "encode") {
            result = EncodePolynom(polynom);
        } else if (action == "decode") {
            result = DecodePolynom(polynom);
        } else
            ythrow TCodedException(400) << "unknown action " << action;
        request.Output() << "HTTP/1.1 200 \r\n";
        request.Output() << "Content-Type:text/plain; charset=utf-8\r\n";
        request.Output() << "\r\n";
        request.Output() << result;
        return true;
    }

    TString TScriptFmlOps::EncodePolynom(const TString& hr) const {
        SRelevanceFormula poly;
        for (size_t beg = 0, end = hr.find_first_of("+-"); beg < hr.size(); beg = end, end = hr.find_first_of("+-", end + 1)) {
            TString monom = hr.substr(beg, end - beg);
            if (!monom)
                continue;
            float weight = monom[0] == '-' ? -1.f : 1.f;
            TVector<ui32> factors;
            TVector<TString> multiplayers = StringSplitter(monom).SplitBySet("+-*").SkipEmpty();
            for (auto& mt : multiplayers) {
                StripInPlace(mt);
                if (mt.find_first_not_of("0123456789.") == TString::npos) {
                    weight *= FromString<float>(mt);
                } else {
                    auto i = RelevHelper.GetIndexByName().find(mt);
                    ui32 index;
                    if (i != RelevHelper.GetIndexByName().end()) {
                        index = i->second;
                    } else if (mt.StartsWith(F_FACTOR_PREFIX) && mt.EndsWith(']')) {
                        if (!TryFromString(mt.substr(F_FACTOR_PREFIX.size(), mt.size() - F_FACTOR_PREFIX.size() - 1), index))
                            ythrow TCodedException(400) << "invalid factor index " << mt;
                    } else
                        ythrow TCodedException(400) << "unknown factor " << mt;
                    factors.push_back(index);
                }
            }
            poly.AddSlag(factors, weight);
        }
        return Encode(poly);
    }

    TString TScriptFmlOps::DecodePolynom(const TString& coded) const {
        SRelevanceFormula poly;
        Decode(&poly, coded, Max<int>());
        TVector<TVector<ui32> > factors;
        TVector<float> weights;
        poly.GetFormula(&factors, &weights);
        TStringStream result;
        for (ui32 i = 0; i < weights.size(); ++i) {
            if (weights[i] >= 0 && i > 0)
                result << "+";
            result << Prec(weights[i], PREC_AUTO);
            for (ui32 index : factors[i]) {
                result << "*";
                auto name = RelevHelper.GetNameByIndex().find(index);
                if (name == RelevHelper.GetNameByIndex().end())
                    result << F_FACTOR_PREFIX << index << "]";
                else
                    result << name->second;
            }
        }
        return result.Str();
    }

    TScriptFmlOps::TFactory::TRegistrator<TScriptFmlOps> Registrator("fml_ops");

};
