#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/select.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT	47350
#define K	1024

fd_set sfds;
int highfd = 0;
struct  client {
  int fd, dead;
  struct client *next;
} *clients;

void newclient(int sock) {
  struct client *c;
  int fd;

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

  printf("New connection on %d\n", fd);
  c = malloc(sizeof(struct client));
  c->fd = fd;
  c->dead = 0;
  c->next = clients;
  clients = c;

  if (fd+1 > highfd) highfd = fd+1;
  FD_SET(fd, &sfds);
}

struct client *disconnect(int fd) {
  struct client *c, *p;
  
  for(p = c = clients; c; c=c->next) {
    if (c->fd == fd) {
      printf("Disconnecting %d\n", fd);
      if (p == c) clients = c->next;
      else p->next = c->next;
      p = c->next;
      close(c->fd);
      FD_CLR(c->fd, &sfds);
      free(c);
      return p;
    }
    p = c;
  }
}

void broadcast(int from, char *msg, int len) {
  struct client *c = clients;

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

  for(c=clients; c != NULL; c=c->next) {
    if (c->dead || from == c->fd) continue;
    if (write(c->fd, msg, len) < 0) c->dead = 1;
  }
}

int main(int argc, char *argv[])
{
  struct sockaddr_in server;
  struct client *c;
  char buf[K];
  int sock, r;
  fd_set rfds;

  clients = NULL;

  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);
  }

  FD_ZERO(&sfds);
  FD_SET(sock, &sfds);
  highfd = sock+1;

  for(;;) {
    // Re-initialize fd set:
    memcpy(&rfds, &sfds, sizeof(sfds));
    
    r = select(highfd, &rfds, NULL, NULL, NULL);
    if (r <= 0) continue;
    
    if (FD_ISSET(sock, &rfds)) newclient(sock);
    for(c=clients; c;) {
      if (c->dead) {
	c = disconnect(c->fd);
	continue;
      }
      if (FD_ISSET(c->fd, &rfds)) {
	r = read(c->fd, buf, K);
	if (r <= 0) {
	  c = disconnect(c->fd);
	  continue;
	}
	broadcast(c->fd, buf, r);
      }
      c = c->next;
    }
  }
  // Not reached:
  return 0;
}
