#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <arpa/inet.h>

int fd_srv = -1;

#define FAILED(msg) perror(msg); \
if (fd_srv > -1) close(fd_srv); \
return -1;

#define BACKLOG 5
#define EPOLL_SIZE 65535
#define EPOLL_EVENTS_SIZE 1024
#define READ_BUFFER_SIZE 1024

int main(int argc, char *argv[])
{
	const char *host;
	int port;
	struct sockaddr_in addr_srv = {}, addr_cli = {};
	int fd_cli;
	struct pollfd poll_fds[256] = {};
	nfds_t poll_fds_n = 0;
	int poll_ret, i;
	socklen_t n_cli;
	char buffer[READ_BUFFER_SIZE] = {};
	ssize_t n_read, n_write;

	if (argc < 3)
	{
		printf("Usage: %s SERVER_IP SERVER_PORT\n", argv[0]);
		return -1;
	}
	host = argv[1];
	port = atoi(argv[2]);
	addr_srv.sin_family = AF_INET;
	addr_srv.sin_port = htons(port);
	addr_srv.sin_addr.s_addr = inet_addr(host);

	if ((fd_srv = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		FAILED("socket()");
	}

	if ((bind(fd_srv, (struct sockaddr *)&addr_srv, sizeof(struct sockaddr_in))) == -1)
	{
		FAILED("bind()");
	}

	if (listen(fd_srv, BACKLOG) == -1)
	{
		FAILED("bind()");
	}

	for (i = 0; i < 256; i++)
	{
		poll_fds[i].fd = -1;
	}
	poll_fds[0].fd = fd_srv;
	poll_fds[0].events = POLLIN;
	poll_fds_n++;

	while (1)
	{
		poll_ret = poll(poll_fds, poll_fds_n, -1);
		if (poll_ret == -1)
		{
			if (errno != EINTR)
			{
				FAILED("poll()");
			}
			continue;
		}

		for (i = 0; i < poll_fds_n; i++)
		{
			if (poll_fds[i].revents == 0)
			{
				continue;
			}

			if (poll_fds[i].revents & POLLIN)
			{
				if (poll_fds[i].fd == fd_srv)
				{
					if ((fd_cli = accept(fd_srv, (struct sockaddr *)&addr_cli, &n_cli)) == -1)
					{
						FAILED("accept()");
					}
					poll_fds[poll_fds_n].fd = fd_cli;
					poll_fds[poll_fds_n].events = POLLIN;
					poll_fds_n++;
				}
				else
				{
					n_read = read(fd_cli, buffer, READ_BUFFER_SIZE);
					if (n_read < 0 && errno != EAGAIN)
					{
						perror("read()");
						close(poll_fds[i].fd);
						poll_fds[i].fd = -1;
					}
					else if (n_read == 0)
					{
						close(poll_fds[i].fd);
						poll_fds[i].fd = -1;
					}
					else
					{
						n_write = write(fd_cli, buffer, n_read);
					}
				}
			}
		}
	}

	close(fd_srv);

	return 0;
}