#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(struct client *dead) {
  struct client *c, *p;

  for(p = c = clients; c; c = c->next) {
    if (c == dead) break;
    p = c;
  }
  printf("Disconnecting %d\n", dead->fd);
  if (p == dead) clients = dead->next;
  else p->next = dead->next;
  p = dead->next;

  close(dead->fd);
  FD_CLR(dead->fd, &sfds);
  free(dead);

  return p;
}

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;
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r)) < 0)
    perror("setsockopt");
  
  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;

  printf("Server starting...\n");

  for(;;) {
    // Re-initialize fd set:
    memcpy(&rfds, &sfds, sizeof(fd_set));

    r = select(highfd, &rfds, NULL, NULL, NULL);
    if (r < 0) perror("select");
    if (r <= 0) continue;

    if (FD_ISSET(sock, &rfds)) newclient(sock);
    for(c=clients; c;) {
      if (c->dead) {
	c = disconnect(c);
	continue;
      }
      if (FD_ISSET(c->fd, &rfds)) {
	r = read(c->fd, buf, K);
	if (r <= 0) {
	  c = disconnect(c);
	  continue;
	}
	broadcast(c->fd, buf, r);
      }
      c = c->next;
    }
  }
  // Not reached:
  return 0;
}
