#include "grouping_common_param.h"
#include "algorithm.h"
#include "plainer.h"

#include <library/cpp/json/json_writer.h>
#include <util/string/vector.h>
#include <util/generic/maybe.h>

namespace NPrivate {

    TGroupingCommonParam::TGroupingCommonParam(const TString& expresion) {
        TVector<TString> eq = SplitString(expresion, "=", 2);
        if (eq.size() < 2)
            Params.insert(eq[0]);
        else {
            TVector<TString> summ = SplitString(eq[1], "+");
            Params.insert(summ.begin(), summ.end());
        }
        Name = eq[0];
    }

    TGroupingCommonParam::TPtr TGroupingCommonParam::Create(const TString& str) {
        TVector<TString> v = SplitString(str, "@");
        return TFactory::Construct(v.size() < 2 ? TString("first") : v[1], v[0]);
    }

    bool TGroupingCommonParam::UpdateParamValueFromElement(NJson::TJsonValue& paramValue, const NJson::TJsonValue& element) const {
        NJson::TJsonValue value;
        const NJson::TJsonValue* calculatedValue = nullptr;
        for (const auto& par : Params) {
            NJson::TJsonValue::TMapType::const_iterator i = element.GetMap().find(par);
            if (i != element.GetMap().end()) {
                if (calculatedValue) {
                    value = i->second.GetIntegerRobust() + calculatedValue->GetIntegerRobust();
                    calculatedValue = &value;
                }
                else
                    calculatedValue = &i->second;
            }
        }
        return calculatedValue && DoUpdateParamValue(paramValue, *calculatedValue);
    }

    void TGroupingCommonParam::InsertParamValueToGroup(NJson::TJsonValue& group, const NJson::TJsonValue& paramValue) const {
        group.InsertValue(Name, paramValue);
    }

    class TGroupingCommonParamFirst : public TGroupingCommonParam {
    public:
        TGroupingCommonParamFirst(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamFirst> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const override {
            paramValue = calculatedValue;
            return true;
        }
    };
    TGroupingCommonParamFirst::TFactory::TRegistrator<TGroupingCommonParamFirst> TGroupingCommonParamFirst::Registrator("first");

    class TGroupingCommonParamSumm : public TGroupingCommonParam {
    public:
        TGroupingCommonParamSumm(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamSumm> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const  override {
            if (calculatedValue.IsDouble() || paramValue.IsDouble()) {
                double v1 = calculatedValue.IsDouble() ? calculatedValue.GetDouble() : calculatedValue.GetIntegerRobust();
                double v2 = paramValue.IsDouble() ? paramValue.GetDouble() : paramValue.GetIntegerRobust();
                paramValue = v1 + v2;
            } else {
                paramValue = calculatedValue.GetIntegerRobust() + paramValue.GetIntegerRobust();
            }
            return false;
        }
    };
    TGroupingCommonParamSumm::TFactory::TRegistrator<TGroupingCommonParamSumm> TGroupingCommonParamSumm::Registrator("summ");

    class TGroupingCommonParamMax : public TGroupingCommonParam {
    public:
        TGroupingCommonParamMax(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamMax> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const  override {
            if (calculatedValue.IsDouble() || paramValue.IsDouble()) {
                double v1 = calculatedValue.IsDouble() ? calculatedValue.GetDouble() : calculatedValue.GetIntegerRobust();
                double v2 = paramValue.IsDouble() ? paramValue.GetDouble() : paramValue.GetIntegerRobust();
                paramValue = Max(v1, v2);
            } else {
                paramValue = Max(calculatedValue.GetIntegerRobust(), paramValue.GetIntegerRobust());
            }
            return false;
        }
    };
    TGroupingCommonParamMax::TFactory::TRegistrator<TGroupingCommonParamMax> TGroupingCommonParamMax::Registrator("max");


    class TGroupingCommonParamMin : public TGroupingCommonParam {
    public:
        TGroupingCommonParamMin(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamMin> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const override {
            TMaybe<double> v1 = Nothing();
            TMaybe<double> v2 = Nothing();
            if (calculatedValue.IsDouble()) {
                v1 = calculatedValue.GetDouble();
            } else if (calculatedValue.IsDefined()) {
                v1 = calculatedValue.GetIntegerRobust();
            }

            if (paramValue.IsDouble()) {
                v2 = paramValue.GetDouble();
            } else if (paramValue.IsDefined()) {
                v2 = paramValue.GetIntegerRobust();
            }
            if (v1.Defined() && v2.Defined()) {
                paramValue = Min(v1.GetRef(), v2.GetRef());
            } else {
                if (v1.Defined()) {
                    paramValue = v1.GetRef();
                }
            }
            return false;
        }
    };
    TGroupingCommonParamMax::TFactory::TRegistrator<TGroupingCommonParamMin> TGroupingCommonParamMin::Registrator("min");


    class TGroupingCommonParamFacet : public TGroupingCommonParam {
    public:
        TGroupingCommonParamFacet(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamFacet> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const  override {
            if (calculatedValue.IsMap()) {
                for (const auto& v : calculatedValue.GetMap()) {
                    NJson::TJsonValue& p = paramValue[v.first];
                    p = p.GetIntegerRobust() + v.second.GetIntegerRobust();
                }
            } else if (!calculatedValue.IsNull()) {
                NJson::TJsonValue& p = paramValue[calculatedValue.GetStringRobust()];
                p = p.GetIntegerRobust() + 1;
            }
            return false;
        }
    };
    TGroupingCommonParamFacet::TFactory::TRegistrator<TGroupingCommonParamFacet> TGroupingCommonParamFacet::Registrator("facet");

    class TGroupingCommonParamFacetJson : public TGroupingCommonParam {
    public:
        using TGroupingCommonParam::TGroupingCommonParam;

        static TFactory::TRegistrator<TGroupingCommonParamFacetJson> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const  override {
            if (calculatedValue.IsString()) {
                auto unpacked = StringToJson(calculatedValue.GetStringRobust());
                if (unpacked.IsMap()) {
                    for (const auto& v : unpacked.GetMap()) {
                        NJson::TJsonValue& p = paramValue[v.first];
                        p = p.GetIntegerRobust() + v.second.GetIntegerRobust();
                    }
                }
            }
            return false;
        }
    };
    TGroupingCommonParamFacetJson::TFactory::TRegistrator<TGroupingCommonParamFacetJson> TGroupingCommonParamFacetJson::Registrator("facet_json");

    class TGroupingCommonParamAverage : public TGroupingCommonParamFacet {
    public:
        TGroupingCommonParamAverage(const TString& expresion)
            : TGroupingCommonParamFacet(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamAverage> Registrator;
        void InsertParamValueToGroup(NJson::TJsonValue& group, const NJson::TJsonValue& paramValue) const override {
            if (paramValue.IsMap()) {
                TCountByValue countByValue;
                for (const auto& v : paramValue.GetMap())
                    countByValue[FromString<ui64>(v.first)] += v.second.GetUIntegerRobust();
                group.InsertValue(GetName(), AverageValue(countByValue));
            }
        }
    };
    TGroupingCommonParamAverage::TFactory::TRegistrator<TGroupingCommonParamAverage> TGroupingCommonParamAverage::Registrator("average");

    class TGroupingCommonParamFacetFlatMin : public TGroupingCommonParam {
    public:
        TGroupingCommonParamFacetFlatMin(const TString& expresion)
            : TGroupingCommonParam(expresion)
        {}
        static TFactory::TRegistrator<TGroupingCommonParamFacetFlatMin> Registrator;
    protected:
        bool DoUpdateParamValue(NJson::TJsonValue& paramValue, const NJson::TJsonValue& calculatedValue) const  override {
            if (calculatedValue.IsMap()) {
                for (const auto& v : calculatedValue.GetMap()) {
                    if (v.second.IsInteger()) {
                        if (!paramValue.IsDefined() || v.second.GetInteger() < paramValue.GetIntegerRobust()) {
                            paramValue = v.second.GetInteger();
                        }
                    }
                }
            }
            return false;
        }
    };
    TGroupingCommonParamFacetFlatMin::TFactory::TRegistrator<TGroupingCommonParamFacetFlatMin> TGroupingCommonParamFacetFlatMin::Registrator("facet_flat_min");

}
