#include <errno.h>
#include "scheduler.hpp"
#include <unistd.h>

void Scheduler::ScheduleFileDescriptor(int fd, int eventFlags, bool isReschedule)
{
	if (fd < 0)
	{
		logger.Warning("Attempted to schedule a non valid filedescriptor");
	}

	epoll_event event;
	event.data.fd = fd;
	event.events = eventFlags;
	int const epollCtl = isReschedule ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
	if (epoll_ctl(epollFileDescriptor, epollCtl, fd, &event) < 0)
	{
		logger.Fatal(std::to_string(errno));
		throw std::runtime_error("Error registering filedescriptor with epoll facilities");
	}
}

std::vector<int> Scheduler::WaitForEvents()
{
	ScheduleHelper scheduleHelper(*this);

	int const eventsHappened = epoll_wait(epollFileDescriptor, epollEvents.data(), static_cast<int>(epollEvents.size()), -1);
	std::vector<int> readyFileDescriptors;
	if (eventsHappened > 0)
	{
		readyFileDescriptors.resize(eventsHappened);
		for (int i = 0; i < eventsHappened; ++i)
		{
			readyFileDescriptors[i] = epollEvents[i].data.fd;
		}
	}

	return readyFileDescriptors;
}

ScheduleHelper Scheduler::GetScheduleHelper()
{
	return ScheduleHelper(*this);
}

Scheduler::Scheduler(Logger & _logger)
	: logger(_logger)
{
	epollFileDescriptor = epoll_create1(0);
	if (epollFileDescriptor < 0)
	{
		throw std::runtime_error("Error creating epoll file descriptor");
	}

	epollEvents.resize(512);
}

Scheduler::~Scheduler()
{
	if (epollFileDescriptor >= 0)
	{
		close(epollFileDescriptor);
	}
}

void ScheduleHelper::ScheduleReadFileDescriptor(int fd, bool oneshot, bool isReschedule)
{
	int flags = EPOLLIN;
	if (oneshot)
	{
		flags |= EPOLLONESHOT;
	}
	scheduler.ScheduleFileDescriptor(fd, flags, isReschedule);
}

void ScheduleHelper::ScheduleWriteFileDescriptor(int fd, bool oneshot, bool isReschedule)
{
	int flags = EPOLLOUT;
	if (oneshot)
	{
		flags |= EPOLLONESHOT;
	}
	scheduler.ScheduleFileDescriptor(fd, flags, isReschedule);
}

ScheduleHelper::ScheduleHelper(Scheduler & _scheduler)
	: scheduler(_scheduler)
{
}