#include "../http/mime.hpp"
#include "../logger.hpp"
#include <fstream>
#include <ios>
#include "staticcontent.hpp"
#include <sstream>
#include <unistd.h>

namespace Middleware
{
	bool TryReadAllBytes(std::string const & filePath, std::vector<char> & buffer)
	{
		std::ifstream ifs(filePath, std::ios_base::binary | std::ios_base::ate);
		if (!ifs.is_open())
		{
			return false;
		}

    std::ifstream::pos_type length = ifs.tellg();

		auto const oldBufferSize = buffer.size();
    buffer.resize(oldBufferSize + length);

    ifs.seekg(0, std::ios_base::beg);
    ifs.read(&buffer[oldBufferSize], length);

		return true;
	}

	bool ContainsDoubleDots(std::string const & s)
	{
		bool previousWasDot = false;
		for(unsigned i = 0; i < s.size(); ++i)
		{
			if (s[i] == '.')
			{
				if(previousWasDot)
				{
					return true;
				}
				previousWasDot = true;
			}
			else
			{
				previousWasDot = false;
			}
		}

		return false;
	}

	void StaticContent::HandleRequest(State & state, ScheduleHelper & scheduleHelper)
	{
		if (!(state.request.type == HttpRequest::Type::GET || state.request.type == HttpRequest::Type::HEAD))
		{
			return;
		}

		std::string path;
		if (state.request.url.HasPath())
		{
			path = root + state.request.url.GetPath();
		}
		else
		{
			// TODO make configurable?
			path = root + "/index.html"; 
		}

		if (ContainsDoubleDots(state.request.url.GetPath()))
		{
			// We cannot deal with this, we are not going to bother checking if
			// this double dot escapes our root directory
			return;
		}

		if (access(path.c_str(), F_OK) != 0)
		{
			// File does not exist
			return;
		}

		state.response.code = HttpResponse::Code::OK;
		state.response.contentType = Http::GetMimeType(path);

		// Regular file descriptors are not supported by epoll, so we have to "just read it"
		if (state.request.type == HttpRequest::Type::GET && !TryReadAllBytes(path, state.response.content))
		{
			state.response.code = HttpResponse::Code::INTERNAL_SERVER_ERROR;
			state.response.contentType = Http::GetMimeType("");
		}

		// HEAD request
		state.finished = true;

		return;
	}

	StaticContent::StaticContent(Logger & _logger, std::string const & staticFileRoot)
		: BaseMiddleware(_logger),
		root(staticFileRoot)
	{
		std::stringstream ss;
		ss << "Using static file root " << root;
		_logger.Info(ss.str());
	}
}