#include "middleware.hpp"
#include "notfound.hpp"
#include "staticcontent.hpp"
#include "unimplemented.hpp"
#include <unistd.h>

namespace Middleware
{
	MiddlewareResult::MiddlewareResult(bool const _writeResult, int const _socketFd)
		: writeResult(_writeResult),
		socketFd(_socketFd)
	{
	}

	Middleware::Middleware(Logger & _logger)
		: logger(_logger)
	{
	}

	MiddlewareResult Middleware::Loop(State & state, ScheduleHelper & scheduleHelper)
	{
		for(size_t i = state.middlewareIndex; i < middlewares.size(); ++i)
		{
			middlewares[i]->HandleRequest(state, scheduleHelper);
			if (state.finished)
			{
				break;
			}

			if (state.awaiting)
			{
				state.middlewareIndex = i;
				eventFdToSocketFdMap[state.awaitingFd] = state.socketFd;
				return MiddlewareResult (false, state.socketFd);
			}
		}

		int const socketFd = state.socketFd;
		auto stateResult = socketFdToStateMap.find(socketFd);
		if (stateResult != socketFdToStateMap.end())
		{
			socketFdToStateMap.erase(stateResult);
			// The state parameter is now invalid!!
		}

		return MiddlewareResult(true, socketFd);
	}

	MiddlewareResult Middleware::Start(int const socketFd, ScheduleHelper & scheduleHelper, Http::Request const & request, Http::Response & response)
	{
		auto & statePtr = socketFdToStateMap[socketFd];
		statePtr = std::make_unique<State>(socketFd, request, response);

		return Loop(*statePtr, scheduleHelper);
	}
	
	MiddlewareResult Middleware::Continue(int const fd, ScheduleHelper & scheduleHelper)
	{
		auto socketFdResult = eventFdToSocketFdMap.find(fd);
		if (socketFdResult == eventFdToSocketFdMap.end())
		{
			close(fd);
			logger.Warning("Dangeling filedescriptor closed in middleware");
			return MiddlewareResult(false, -1);
		}

		int const socketFd = socketFdResult->second;
		eventFdToSocketFdMap.erase(socketFdResult);

		auto stateResult = socketFdToStateMap.find(socketFd);
		if (stateResult == socketFdToStateMap.end())
		{
			close(fd);
			close(socketFd);
			logger.Warning("Stateless socket file descriptor and associated event file descriptor closed");
			return MiddlewareResult(false, -1);
		}

		return Loop(*(stateResult->second), scheduleHelper);
	}

	void Middleware::UseMiddleware(std::unique_ptr<BaseMiddleware> && middleware)
	{
		middlewares.emplace_back(std::move(middleware));
	}

	Middleware Middleware::CreateWithStaticFile(Logger & logger, std::string const & rootDirectory)
	{
		Middleware middleware(logger);
		middleware.UseMiddleware(std::make_unique<StaticContent>(logger, rootDirectory));
		middleware.UseMiddleware(std::make_unique<NotFound>(logger));
		middleware.UseMiddleware(std::make_unique<Unimplemented>(logger));

		return middleware;
	}
}