#include <stddef.h>
#include <string>

#include "httpinput.h"
#include "input.h"
#include "state.pb.h"
#include "udpinput.h"

using namespace std;

namespace {

// Does not support passwords, only user:host, since this is really only used
// to parse VLC's udp://source@multicastgroup:1234/ syntax (we do not support
// even basic auth).
void split_user_host(const string &user_host, string *user, string *host)
{
	size_t split = user_host.find("@");
	if (split == string::npos) {
		user->clear();
		*host = user_host;
	} else {
		*user = string(user_host.begin(), user_host.begin() + split);
		*host = string(user_host.begin() + split + 1, user_host.end());
	}
}

}  // namespace

// Extremely rudimentary URL parsing.
bool parse_url(const string &url, string *protocol, string *user, string *host, string *port, string *path)
{
	// pipe:foo (or pipe:"foo").
	if (url.find("pipe:") == 0) {
		*protocol = "pipe";
		*path = string(url.begin() + 5, url.end());
		return true;
	}

	size_t split = url.find("://");
	if (split == string::npos) {
		return false;
	}
	*protocol = string(url.begin(), url.begin() + split);

	string rest = string(url.begin() + split + 3, url.end());

	// Split at the first slash, or the first colon that's not within [].
	bool within_brackets = false;
	for (split = 0; split < rest.size(); ++split) {
		if (rest[split] == '[') {
			if (within_brackets) {
				// Can't nest brackets.
				return false;
			}
			within_brackets = true;
		} else if (rest[split] == ']') {
			if (!within_brackets) {
				// ] without matching [.
				return false;
			}
			within_brackets = false;
		} else if (rest[split] == '/') {
			break;
		} else if (rest[split] == ':' && !within_brackets) {
			break;
		}
	}

	if (split == rest.size()) {
		// http://foo
		split_user_host(rest, user, host);
		*port = *protocol;
		*path = "/";
		return true;
	}

	split_user_host(string(rest.begin(), rest.begin() + split), user, host);
	char ch = rest[split];  // Colon or slash.
	rest = string(rest.begin() + split + 1, rest.end());

	if (ch == ':') {
		// Parse the port.
		split = rest.find_first_of('/');
		if (split == string::npos) {
			// http://foo:1234
			*port = rest;
			*path = "/";
			return true;
		} else {
			// http://foo:1234/bar
			*port = string(rest.begin(), rest.begin() + split);
			*path = string(rest.begin() + split, rest.end());
			return true;
		}
	}

	// http://foo/bar
	*port = *protocol;
	*path = "/" + rest;
	return true;
}

Input *create_input(const string &url, Input::Encoding encoding)
{
	string protocol, user, host, port, path;
	if (!parse_url(url, &protocol, &user, &host, &port, &path)) {
		return nullptr;
	}
	if (protocol == "http" || protocol == "pipe") {
		return new HTTPInput(url, encoding);
	}
	if (protocol == "udp") {
		assert(encoding == Input::INPUT_ENCODING_RAW);
		return new UDPInput(url);
	}
	return nullptr;
}

Input *create_input(const InputProto &serialized)
{
	string protocol, user, host, port, path;
	if (!parse_url(serialized.url(), &protocol, &user, &host, &port, &path)) {
		return nullptr;
	}
	if (protocol == "http" || protocol == "pipe") {
		return new HTTPInput(serialized);
	}
	if (protocol == "udp") {
		return new UDPInput(serialized);
	}
	return nullptr;
}

Input::~Input() {}

