#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT	47350
#define K	1024

#define MAXCLIENTS	1024

int epfd;
int client[K], nclients = 0;

void newclient(int sock) {
  struct epoll_event epv;
  int i, fd;

  fd = accept(sock, NULL, NULL);
  if (fd < 0) {
    perror("accept");
    return;
  }

  printf("New connection on %d\n", fd);
  epv.data.fd = nclients;
  epv.events = EPOLLIN;
  epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epv);
  client[nclients++] = fd;
}

struct client *disconnect(int idx)
{
  printf("Disconnecting %d\n", client[idx]);
  epoll_ctl(epfd, EPOLL_CTL_DEL, client[idx], NULL);
  close(client[idx]);
  for(; idx < nclients-1; idx++) {
    client[idx] = client[idx+1];
  }
  nclients--;
}

void broadcast(int from, char *msg, int len) {
  int i;

  printf("->%d) %.*s", from, len, msg);

  for(i=0; i < nclients;) {
    if (write(client[i], msg, len) < 0) {
      disconnect(i);
      continue;
    }
    i++;
  }
}

int main(int argc, char *argv[])
{
  struct sockaddr_in server;
  struct epoll_event epv;
  char buf[K];
  int sock, r, i;

  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    perror("socket");
    exit(1);
  }
  r = 1;
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r));
  
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(PORT);
  if (bind(sock, (struct sockaddr *)&server, sizeof(server))) {
    perror("bind");
    exit(1);
  }
  if (listen(sock, 5) < 0) {
    perror("listen");
    exit(1);
  }

  if ((epfd = epoll_create(25)) < 0) {
    perror("epoll_create");
    exit(1);
  }
  epv.events = EPOLLIN;
  epv.data.fd = -1;
  epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &epv);

  for(;;) {
    r = epoll_wait(epfd, &epv, 1, -1);
    if (r <= 0) continue;

    if (epv.events & (EPOLLERR | EPOLLHUP)) {
      if (epv.data.fd == -1) break;
      else disconnect(epv.data.fd);
    } else {
      if (epv.data.fd == -1) newclient(sock);
      else {
	r = read(client[epv.data.fd], buf, K);
	if (r <= 0) {
	  disconnect(epv.data.fd);
	  continue;
	}
	broadcast(client[epv.data.fd], buf, r);
      }
    }
  }
  // Not reached:
  return 0;
}
