
#include "Webserv.hpp"

#define MAX_EVENTS 42 // arbitrary
#define TIMEOUT 3000


#define BUFSIZE 8192 // (8Ko)
#define STATUS_500 std::string("Status: 500" CRLF CRLF);
void	Webserv::_read_cgi_output(Client *client)
{
	char	buf[BUFSIZE];
	ssize_t	ret;
	std::cerr << "_read_cgi_output()" << "\n";
	std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
	std::cerr << "client fd = " << client->get_cl_fd() << "\n";
	std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n";

	ret = ::read(client->cgi_pipe_rfd, buf, BUFSIZE);
	std::cerr << "cgi read ret = " << ret << "\n";
	if (ret == -1)
	{
		std::perror("err read(cgi_fd)");
		client->cgi_output = STATUS_500;
	}
	else if (ret == 0)
		std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n";
	else
	{
		std::cerr << "NORMAL BEHAVIOR I THINK!\n"; // debug
		client->cgi_output.append(buf, ret);
	}
}

void	Webserv::_handle_epoll_error_cgi_fd(uint32_t events, Client *client)
{
	(void)events;
	std::cerr << "cgi EPOLLERR" << "\n";
	std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
	std::cerr << "client fd = " << client->get_cl_fd() << "\n";
	std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n";

	client->cgi_output = STATUS_500;

	pid_t	wait_ret;
	wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
	std::cerr << "cgi EPOLLERR waitpid ret = " << wait_ret << "\n";
	if (wait_ret == client->cgi_pid)
	{
		if (client->cgi_pipe_rfd)
		{
			if (::close(client->cgi_pipe_rfd) == -1)
				std::perror("err close()");
		}
		client->cgi_pipe_rfd = 0;
		_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
	}

}

void	Webserv::_cgi_epollhup(uint32_t events, Client *client)
{
	(void)events;
	(void)client;

/* 	std::cerr << "cgi EPOLLHUP" << "\n";
	std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
	std::cerr << "client fd = " << client->get_cl_fd() << "\n";
	std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; */

	pid_t	wait_ret;
	wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
	// std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n";
	if (wait_ret == client->cgi_pid)
	{
		if (client->cgi_pipe_rfd)
		{
			if (::close(client->cgi_pipe_rfd) == -1)
				std::perror("err close()");
		}
		client->cgi_pipe_rfd = 0;
		_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
	}
}

Client	*Webserv::_find_cgi_fd(int cgi_fd)
{
	std::vector<Client>::iterator	it = _clients.begin();
	std::vector<Client>::iterator	it_end = _clients.end();
	while (it != it_end)
	{
		if (it->cgi_pipe_rfd == cgi_fd)
			return (&(*it));
		++it;
	}
	return (NULL);
}

void	Webserv::run()
{
	std::cerr << "Server started\n";
	struct epoll_event	events[MAX_EVENTS];
	int	nfds;
	int	i;
	int	count_loop = 0;
	std::vector<listen_socket>::iterator	it_lsocket;
	Client	*client_cgi = NULL;

	g_run = true;
	while (g_run)
	{
		std::cerr << ++count_loop << "----loop epoll() ";
		nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
		std::cerr << "(nfds=" << nfds << ")\n";
		if (nfds == -1)
		{
			int errno_copy = errno;
			std::perror("err epoll_wait()");
			if (errno_copy == EINTR)
				g_run = false;
			else
				throw std::runtime_error("Epoll wait");
		}
		else if (nfds == 0 && !_clients.empty())
			_timeout();
		i = 0;
		while (i < nfds)
		{
			try {
				it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
				client_cgi = _find_cgi_fd(events[i].data.fd);
				if (it_lsocket != _listen_sockets.end())
				{
					if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
						_handle_epoll_error_lsocket(events[i].events, it_lsocket);
					else if (events[i].events & EPOLLIN)
						_accept_connection(*it_lsocket);
				}
				else if (client_cgi)
				{
					if (events[i].events & EPOLLERR)
						_handle_epoll_error_cgi_fd(events[i].events, client_cgi);
					else if (events[i].events & EPOLLIN)
						_read_cgi_output(client_cgi);
					else if ( (events[i].events & EPOLLHUP) && !(events[i].events & EPOLLIN) )
						_cgi_epollhup(events[i].events, client_cgi);
				}
				else if (std::find(_clients.begin(), _clients.end(), events[i].data.fd) != _clients.end()) // TODO: save the it in var to avoid multiples find()
				{
					if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
						_handle_epoll_error_client(events[i].events, events[i].data.fd);
					else if (events[i].events & EPOLLIN)
						_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
					else if (events[i].events & EPOLLOUT)
						_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
				}
				++i;
				if (!g_run)
					break;
			}
			catch (const std::bad_alloc& e) {
				std::cerr << e.what() << '\n';
				_close_all_clients();
				/* Swap to free the memory
				From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */
				std::vector<Client>().swap(_clients);
				break;
			}
			catch (const Webserv::ExecFail& e)
			{
				std::cerr << e.what() << '\n';
				throw;
			}
			catch (const std::exception& e) {
				std::cerr << e.what() << '\n';
				++i;
			}
		}
	}
}
