#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
     
#define PORT    5555
#define MAXMSG  512
     
int
make_socket (uint16_t port)
{
        int server_sock;
        struct sockaddr_in name;
        int yes = 1;
     
        /* Create the socket. */
        server_sock = socket (PF_INET, SOCK_STREAM, 0);
        if (server_sock < 0)
        {
                perror ("socket");
                exit (EXIT_FAILURE);
        }
     
        // tento radek zpusobi, ze pri opakovanem restartu serveru, bude volani
        // funkce bind() uspesne, kdo neveri, at ho zakomentuje :))
        if(setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
                perror("setsockopt");
                exit(1);
        }

        /* Give the socket a name. */
        name.sin_family = AF_INET;
        name.sin_port = htons (port);
        name.sin_addr.s_addr = htonl (INADDR_ANY);
        if (bind (server_sock, (struct sockaddr *) &name, sizeof (name)) < 0)
        {
                perror ("bind");
                exit (EXIT_FAILURE);
        }
     
        return server_sock;
}

int read_from_client (int filedes)
{
        char buffer[MAXMSG];
        int nbytes;
     
        nbytes = read (filedes, buffer, MAXMSG);
        if (nbytes < 0)
        {
                /* Read error. */
                perror ("read");
                exit (EXIT_FAILURE);
        }
        else if (nbytes == 0)
                /* End-of-file. */
                return -1;
        else
        {
                /* Data read. */
                fprintf (stderr, "Server: got message: `%s'\n", buffer);
                return 0;
        }
}
     
int
main (void)
{
        int server_sock;
        fd_set active_fd_set, read_fd_set;
        int fd;
        struct sockaddr_in clientname;
        size_t size;
     
        /* Create the socket and set it up to accept connections. */
        server_sock = make_socket (PORT);
        if (listen (server_sock, 1) < 0)
        {
                perror ("listen");
                exit (EXIT_FAILURE);
        }
     
        /* Initialize the set of active sockets. */
        FD_ZERO (&active_fd_set);
        FD_SET (server_sock, &active_fd_set);
     
        while (1)
        {
                /* Block until input arrives on one or more active sockets. */
                read_fd_set = active_fd_set;
                if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
                {
                        perror ("select");
                        exit (EXIT_FAILURE);
                }
     
                /* Service all the sockets with input pending. */
                for (fd = 0; fd < FD_SETSIZE; ++fd)
                        if (FD_ISSET (fd, &read_fd_set))
                        {
                                if (fd == server_sock)
                                {
                                        /* Connection request on original socket. */
                                        int new;
                                        size = sizeof (clientname);
                                        new = accept (server_sock,
                                                      (struct sockaddr *) &clientname,
                                                      &size);
                                        if (new < 0)
                                        {
                                                perror ("accept");
                                                exit (EXIT_FAILURE);
                                        }
                                        fprintf (stderr,
                                                 "Server: connect from host %s, port %hd.\n",
                                                 inet_ntoa (clientname.sin_addr),
                                                 ntohs (clientname.sin_port));
                                        FD_SET (new, &active_fd_set); /* Add new client to the set */
                                }
                                else
                                {
                                        /* Data arriving on an already-connected socket. */
                                        if (read_from_client (fd) < 0)
                                        {
                                                /* Client closed the connection */
                                                FD_CLR (fd, &active_fd_set); /* Remove the client from the set */
                                                close (fd);
                                        }
                                }
                        }
        }
}