#include <xs/export.h>
#include <xs/typemap.h>
#include <xs/uri/router.h>
#include <regex>

using namespace xs;
using namespace xs::uri;
using namespace panda;
using namespace panda::uri;
using namespace panda::uri::router;

static void router_add (SvRouter* r, const Scalar& sv_path, const Scalar& value) {
    auto copy = Scalar::create();
    SvSetSV_nosteal(copy, value);
    auto path = xs::in<string>(sv_path);
    if (SvRXOK(sv_path)) {
        if (path.find("(?^") == 0) {
            static std::regex rere("^\\(\\?\\^[a-z]*:");
            auto re = regex_replace(std::string(path.data(), path.length()), rere, "");
            path = string(re.data(), re.length()-1);
        }
        r->add({Regex(path), copy});
    } else {
        r->add({path, copy});
    }
}

MODULE = URI::Router                PACKAGE = URI::Router
PROTOTYPES: DISABLE

BOOT {
    using M = Method;
    Stash s(__PACKAGE__);
    xs::exp::create_constants(s, {
        {"METHOD_UNSPECIFIED", int(M::Unspecified)},
        {"METHOD_OPTIONS",     int(M::Options)    },
        {"METHOD_GET",         int(M::Get)        },
        {"METHOD_HEAD",        int(M::Head)       },
        {"METHOD_POST",        int(M::Post)       },
        {"METHOD_PUT",         int(M::Put)        },
        {"METHOD_DELETE",      int(M::Delete)     },
        {"METHOD_TRACE",       int(M::Trace)      },
        {"METHOD_CONNECT",     int(M::Connect)    },
    });
    xs::exp::autoexport(s);
}

SvRouter* SvRouter::new (...) {
    RETVAL = new SvRouter();
    for (I32 i = 1; i < items - 1; i += 2) {
        router_add(RETVAL, ST(i), ST(i+1));
    }
}

void SvRouter::add (Scalar path, Scalar value) {
    router_add(THIS, path, value);
}

void SvRouter::route (string_view path, Sv sv_method = Sv()) {
    Method method = sv_method ? Method(SvIV(sv_method)) : Method::Get;
    auto opt = THIS->route(path, method);
    if (!opt) XSRETURN_UNDEF;
    auto& res = *opt;
    
    if (GIMME_V != G_ARRAY) {
        XPUSHs(res.value);
        XSRETURN(1);
    }
    else {
        I32 nret = res.captures.size() + 1;
        EXTEND(SP, nret);
        XPUSHs(res.value);
        for (auto& s : res.captures) mXPUSHs(xs::out(s).detach());
        XSRETURN(nret);
    }
}

uint64_t SvRouter::bench (string_view path) {
    RETVAL = 0;
    for (int i = 0; i < 1000; ++i) {
        auto opt = THIS->route(path);
        if (opt) ++RETVAL;
    }
}