#pragma once

#include <functional>
#include <map>
#include <thread>

#include "Common/Net/HTTPHeaders.h"
#include "Common/Net/Resolve.h"

class NewThreadExecutor {
public:
	~NewThreadExecutor();
	void Run(std::function<void()> func);

private:
	std::vector<std::thread> threads_;
};

namespace net {

class InputSink;
class OutputSink;

}  // namespace net

namespace http {

class ServerRequest {
public:
	ServerRequest(int fd);
	~ServerRequest();

	std::string_view resource() const {
		return header_.resource;
	}

	RequestHeader::Method Method() const {
		return header_.method;
	}

	const RequestHeader &Header() const {
		return header_;
	}

	bool GetParamValue(const char *param_name, std::string *value) const {
		return header_.GetParamValue(param_name, value);
	}
	// Use lowercase.
	bool GetHeader(const char *name, std::string *value) const {
		return header_.GetOther(name, value);
	}

	net::InputSink *In() const { return in_; }
	net::OutputSink *Out() const { return out_; }

	// TODO: Remove, in favor of PartialWrite and friends.
	int fd() const { return fd_; }

	void WritePartial() const;
	void Write();
	void Close();

	bool IsOK() const { return fd_ > 0; }

	// If size is negative, no Content-Length: line is written.
	void WriteHttpResponseHeader(const char *ver, int status, int64_t size = -1, const char *mimeType = nullptr, const char *otherHeaders = nullptr) const;

private:
	net::InputSink *in_;
	net::OutputSink *out_;
	RequestHeader header_;
	int fd_;
};

// Register handlers on this class to serve stuff.
class Server {
public:
	// Takes ownership.
	Server(NewThreadExecutor *executor);
	virtual ~Server();

	typedef std::function<void(const ServerRequest &)> UrlHandlerFunc;
	typedef std::map<std::string, UrlHandlerFunc, std::less<>> UrlHandlerMap;

	// Runs forever, serving request. If you want to do something else than serve pages,
	// better put this on a thread. Returns false if failed to start serving, never
	// returns if successful.
	bool Run(int port);
	// May run for (significantly) longer than timeout, but won't wait longer than that
	// for a new connection to handle.
	bool RunSlice(double timeout);
	bool Listen(int port, const char *reason, net::DNSType type = net::DNSType::ANY);
	void Stop();

	void RegisterHandler(const char *url_path, UrlHandlerFunc handler);
	void SetFallbackHandler(UrlHandlerFunc handler);

	// If you want to customize things at a lower level than just a simple path handler,
	// then inherit and override this. Implementations should forward to HandleRequestDefault
	// if they don't recognize the url.
	virtual void HandleRequest(const ServerRequest &request);

	int ListenerSocket() const {
		return listenerSock_;
	}
	int Port() const {
		return port_;
	}
	const std::string &LocalAddress() const {
		return localAddress_;
	}

private:
	bool Listen6(int port, bool ipv6_only, const char *reason);
	bool Listen4(int port, const char *reason);

	void HandleConnection(int conn_fd);

	// Things like default 404, etc.
	void HandleRequestDefault(const ServerRequest &request);

	// Neat built-in handlers that are tied to the server.
	void HandleListing(const ServerRequest &request);
	void Handle404(const ServerRequest &request);

	int listenerSock_;
	int port_ = 0;
	std::string localAddress_;

	UrlHandlerMap handlers_;
	UrlHandlerFunc fallback_;

	NewThreadExecutor *executor_;
};

}  // namespace http
