#include "pihttpserver.h"

#include "piserverendpoint_p.h"


struct Endpoint: public PIHTTP::ServerEndpoint {
	PIHTTP::Method method = PIHTTP::Method::Unknown;
	PIHTTPServer::RequestFunction function;
};


PRIVATE_DEFINITION_START(PIHTTPServer)
	PIMap<uint, PIVector<Endpoint>> endpoints;
PRIVATE_DEFINITION_END(PIHTTPServer)


PIHTTPServer::PIHTTPServer() {
	setRequestCallback([this](const PIHTTP::MessageConst & r) -> PIHTTP::MessageMutable {
		PIHTTP::MessageMutable reply;
		reply.setCode(PIHTTP::Code::NotFound);
		auto in_path = PIHTTP::ServerEndpoint::splitPath(r.path());
		auto it      = PRIVATE->endpoints.makeReverseIterator();
		bool found   = false;
		while (it.next()) {
			for (const auto & ep: it.value()) {
				if (ep.function && ep.method == r.method()) {
					PIMap<PIString, PIString> ext_args;
					if (ep.match(in_path, ext_args)) {
						auto & r_mut(static_cast<PIHTTP::MessageMutable &>(const_cast<PIHTTP::MessageConst &>(r)));
						r_mut.pathArguments() = ext_args;
						r_mut.arguments()     = r_mut.pathArguments();
						r_mut.arguments() << r_mut.queryArguments();
						reply = ep.function(r);
						found = true;
						break;
					}
				}
			}
			if (found) break;
		}
		if (!found && unhandled) reply = unhandled(r);
		auto hit = reply_headers.makeIterator();
		while (hit.next())
			reply.addHeader(hit.key(), hit.value());
		return reply;
	});
}


PIHTTPServer::~PIHTTPServer() {
	stop();
}


bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, RequestFunction functor) {
	Endpoint ep;
	if (!ep.create(path)) return false;
	ep.method   = method;
	ep.function = std::move(functor);
	PRIVATE->endpoints[ep.priority] << ep;
	return true;
}


void PIHTTPServer::registerUnhandled(RequestFunction functor) {
	unhandled = functor;
}


void PIHTTPServer::unregisterPath(const PIString & path, PIHTTP::Method method) {
	auto pl = PIHTTP::ServerEndpoint::splitPath(path);
	auto it = PRIVATE->endpoints.makeIterator();
	while (it.next()) {
		it.value().removeWhere([&pl, method](const Endpoint & ep) { return ep.path == pl && ep.method == method; });
	}
	PRIVATE->endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
}


void PIHTTPServer::unregisterPath(const PIString & path) {
	auto pl = PIHTTP::ServerEndpoint::splitPath(path);
	auto it = PRIVATE->endpoints.makeIterator();
	while (it.next()) {
		it.value().removeWhere([&pl](const Endpoint & ep) { return ep.path == pl; });
	}
	PRIVATE->endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
}
