#include <search/meta/context.h>
#include <search/meta/mergedres.h>
#include <search/meta/rearrange/rearrange.h>
#include <search/web/core/rule.h>

class TStringSortRule : public IRearrangeRule {
    class TStringSortContext : public IRearrangeRuleContext {
    public:
        void DoRearrangeAfterMerge(TRearrangeParams& rearrangeParams) override {
            TMetaGrouping* g = rearrangeParams.Current.Grouping;
            if (!g) {
                return;
            }
            TString attr;
            if (rearrangeParams.RP.SP.SortMethod == SORT_BY_STR_ATTR) {
                attr = GroupAttrPrefixSorting + rearrangeParams.RP.SP.AttrToSort;
            }
            if (TStringBuf(g->GetSortMethod(), PropPrefixJustRearrange.length()) == PropPrefixJustRearrange) {
                attr = g->GetSortMethod() + PropPrefixJustRearrange.length();
            }
            if (attr) {
                TMultiMap<TString, ui32> groups;
                for (size_t ind = 0; ind < g->Size(); ind++) {
                    TMsString attrVal;
                    const auto& group = g->GetMetaGroup(ind);
                    if (!group.MetaDocs.empty()) {
                        auto doc = g->GetMetaGroup(ind).MetaDocs[0];
                        if (doc->FirstStageAttrValues()) {
                            for (const auto& val : doc->FirstStageAttrValues()->GetValues(attr))
                                if (!attrVal || val < attrVal)
                                    attrVal = val;
                        }
                    }
                    groups.emplace(TString(attrVal), ind);
                }
                TVector<size_t> groupsOrdered;
                groupsOrdered.resize(g->Size(), (size_t)-1);
                if (g->SP.QAscendOrder) {
                    ui32 id = 0;
                    for (TMultiMap<TString, ui32>::const_iterator i = groups.begin(), e = groups.end(); i != e; ++i, ++id) {
                        groupsOrdered[id] = i->second;
                    }
                }
                else {
                    ui32 id = 0;
                    for (TMultiMap<TString, ui32>::const_reverse_iterator i = groups.rbegin(), e = groups.rend(); i != e; ++i, ++id) {
                        groupsOrdered[id] = i->second;
                    }
                }
                g->RearrangeGroups(groupsOrdered);
            }
        }
        void DoAdjustClientParams(const TAdjustParams& ap) override {
            if (ap.RP.SP.SortMethod == SORT_BY_STR_ATTR) {
                TString attr = GroupAttrPrefixSorting + ap.RP.SP.AttrToSort;
                if (!ap.ClientRequestAdjuster->ClientFormFieldHas("fsgta", attr)) {
                    ap.ClientRequestAdjuster->ClientFormFieldInsert("fsgta", attr);
                }
                return;
            }
            for (const auto& grouping: ap.RP.GroupingParams) {
                if (grouping.gMode == GM_FLAT
                    && grouping.gAttrToSort.StartsWith(PropPrefixJustRearrange.data(), PropPrefixJustRearrange.size())
                ) {
                    TString attr = grouping.gAttrToSort.data() + PropPrefixJustRearrange.length();
                    if (!ap.ClientRequestAdjuster->ClientFormFieldHas("fsgta", attr)) {
                        ap.ClientRequestAdjuster->ClientFormFieldInsert("fsgta", attr);
                    }
                }
            }
        }
    
        const static TStringBuf PropPrefixJustRearrange;
        const static TStringBuf GroupAttrPrefixSorting;
    };
public:
    inline TStringSortRule(const TString& /*config*/, const TSearchConfig& /*searchConfig*/)
    {
    }

    virtual ~TStringSortRule() {
    }

    IRearrangeRuleContext* DoConstructContext() const override {
        return new TStringSortContext;
    }
};

const TStringBuf TStringSortRule::TStringSortContext::PropPrefixJustRearrange("gta_");
const TStringBuf TStringSortRule::TStringSortContext::GroupAttrPrefixSorting("_Name_");

IRearrangeRule* CreateStringSortRule(const TString& config, const TSearchConfig& searchConfig) {
    return new TStringSortRule(config, searchConfig);
}

REGISTER_REARRANGE_RULE(StringSort, CreateStringSortRule);

