#include "piliterals_string.h"
#include "piserverendpoint_p.h"


PIStringList PIHTTP::ServerEndpoint::splitPath(const PIString & path) {
	auto ret = path.split("/");
	ret.removeAll({});
	return ret;
}


PIHTTP::ServerEndpoint::PathElement::PathElement(const PIString & reg) {
	source = reg;
	if (reg == "*"_a) {
		type = Type::AnyOne;
	} else if (reg == "**"_a) {
		type = Type::AnyMany;
	} else if (reg.contains('*')) {
		type  = Type::AnyPart;
		parts = reg.split('*');
	} else if (reg.contains('{')) {
		type    = Type::Arguments;
		int ind = 0, eind = 0, pind = 0;
		for (;;) {
			ind = reg.find('{', ind);
			if (ind < 0) break;
			eind = reg.find('}', ind + 1);
			if (eind < 0) break;
			arguments.insert(arguments.size_s(), reg.mid(ind + 1, eind - ind - 1));
			if (ind == 0)
				parts << PIString();
			else {
				if (ind > pind)
					parts << reg.mid(pind, ind - pind);
				else if (parts.isNotEmpty()) {
					piCout << "[PIHTTP::ServerEndpoint] Warning: sequential arguments, ignoring this path!";
					type = Type::Invalid;
					return;
				}
			}
			ind = pind = eind + 1;
		}
		if (eind < reg.size_s() - 1) parts << reg.mid(eind + 1);
	}
}


bool PIHTTP::ServerEndpoint::PathElement::match(const PIString & in, PIMap<PIString, PIString> & ext_args) const {
	// piCout << "match" << source << "with" << in;
	if (type == Type::AnyOne) return true;
	if (type == Type::AnyPart) {
		int ind = 0;
		for (const auto & m: parts) {
			ind = in.find(m, ind);
			if (ind < 0) return false;
		}
		return true;
	}
	if (type == Type::Arguments) {
		int ind = 0, eind = 0;
		for (int i = 0; i < parts.size_s(); ++i) {
			const auto & m(parts[i]);
			if (m.isNotEmpty()) {
				ind = in.find(m, eind);
				if (ind < 0) return false;
			}
			if (i > 0) {
				ext_args[arguments.value(i - 1)] = in.mid(eind, ind - eind);
			}
			eind = ind + m.size_s();
		}
		if (parts.size() == arguments.size()) {
			ext_args[arguments.value(arguments.size_s() - 1)] = in.mid(eind);
		}
		return true;
	}
	return source == in;
}


uint PIHTTP::ServerEndpoint::PathElement::priority() const {
	switch (type) {
	case Type::Fixed: return 0x10000; break;
	case Type::Arguments: return 0x1000; break;
	case Type::AnyPart: return 0x100; break;
	case Type::AnyOne: return 0x10; break;
	case Type::AnyMany: return 0x1; break;
	default: break;
	}
	return 0;
}


bool PIHTTP::ServerEndpoint::create(const PIString & p) {
	path = splitPath(p);
	prepared_path.clear();
	priority = 0;
	for (const auto & i: path) {
		PathElement pe(i);
		prepared_path << pe;
		path_types |= pe.type;
		priority += pe.priority();
	}
	return !path_types[PathElement::Type::Invalid];
}


bool PIHTTP::ServerEndpoint::match(const PIStringList & in_path, PIMap<PIString, PIString> & ext_args) const {
	if (path_types[PathElement::Type::AnyMany]) {
		int any_ind = path.indexOf("**"_a);
		for (int i = 0; i < any_ind; ++i) {
			if (!prepared_path[i].match(in_path[i], ext_args)) return false;
		}
		int si = prepared_path.size_s() - 1, ii = in_path.size_s() - 1;
		for (; si > any_ind && ii >= 0; --si, --ii) {
			if (!prepared_path[si].match(in_path[ii], ext_args)) return false;
		}
	} else {
		if (in_path.size() != prepared_path.size()) return false;
		for (int i = 0; i < prepared_path.size_s(); ++i) {
			if (!prepared_path[i].match(in_path[i], ext_args)) return false;
		}
	}
	return true;
}
