#include "mingeo_search.h"

#include <library/cpp/geo/size.h>
#include <library/cpp/geo/point.h>

using namespace NRTYServer;
using namespace NGeo;

namespace {
    constexpr TStringBuf stdDelimiter = ","; // actually, the default delimiter from library/geo
    const char LL[] = "lonlat";
    const char SPN[] = "spn";
    const char MAXSPN[] = "maxspn";
    const char PRNE[] = "prne"; // "expand only"
    const char PRNA[] = "prn"; // "expand and collapse"

    constexpr double DefaultRangeForQueryWithoutSpan = 0.00005;
}

const TString TMinGeoSearchContext::CgiCompName = "geo";

bool TMinGeoSearchContext::ParseContext(const TRTYSearchRequestContext& context, TMessagesCollector& errors) {
    const auto* params = context.GetParameters(CgiCompName);
    if (!params || !params->contains(LL))
        return false;

    TString llValue = params->at(LL);
    TMaybe<TGeoPoint> ll = TGeoPoint::TryParse(llValue, stdDelimiter);
    if (!ll.Defined()) {
        errors.AddMessage("TMinGeoSearchContext", Sprintf("Incorrect '%s' value", LL));
        return false;
    }

    TMaybe<TSize> span;
    auto iter = params->find(SPN);
    if (iter != params->end()) {
        span = TSize::TryParse(iter->second, stdDelimiter);
        if (!span.Defined()) {
            errors.AddMessage("TMinGeoSearchContext", Sprintf("Incorrect '%s' value", SPN));
        }
    } else {
        // Zero span works completely fine, but using a ~10 meter range by default helps us meet the users' expectations
        constexpr double r = DefaultRangeForQueryWithoutSpan;
        span = TSize{r, r};
    }

    if ((iter = params->find(PRNE)) != params->end()) {
        PruningOpts.Mode = pmExpand;
        if (!TryFromString<ui32>(iter->second, PruningOpts.DocCount)) {
            errors.AddMessage("TMinGeoSearchContext", Sprintf("Incorrect '%s' value", PRNE));
        }
    } else if ((iter = params->find(PRNA)) != params->end()) {
        PruningOpts.Mode = pmAuto;
        if (!TryFromString<ui32>(iter->second, PruningOpts.DocCount)) {
            errors.AddMessage("TMinGeoSearchContext", Sprintf("Incorrect '%s' value", PRNA));
        }
    } else {
        PruningOpts.Mode = pmNone;
    }

    if ((iter = params->find(MAXSPN)) != params->end()) {
        if (PruningOpts.Mode == pmNone) {
            errors.AddMessage("TMinGeoSearchContext", Sprintf("'%s' without pruning has no effect", MAXSPN));
        } else {
            PruningOpts.SpanLimit = TSize::TryParse(iter->second, stdDelimiter);
            if (!PruningOpts.SpanLimit.Defined()) {
                errors.AddMessage("TMinGeoSearchContext", Sprintf("Incorrect '%s' value", MAXSPN));
            }
        }
    }

    const TVector<ui64>& keyPrefixes = context.GetRP().KeyPrefixes;
    KeyPrefixes = keyPrefixes.empty() ? TVector<ui64>{0ull} : keyPrefixes;
    UserRequest = NGeo::TGeoWindow(*ll, *span);
    return true;
}

//FIXME(yrum): would be nice to implement other syntaxes here as some "syntax sugar'. See extsearch/geo: RLL, WLL etc.
