/*
  these headers do SO much Its hard to list...
 */

#include <stdio.h>      // well everyone needs stdio!
#include <stdlib.h>     // thow in just for the heck of it
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>  // bind
#include <sys/time.h>
#include <signal.h>
#include <sys/socket.h> // bind
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>


#define DEFAULTPORT 5102  // basis address backwards plus one
#define BUFSIZE 4096      // copied, sounds good to me
// verbose shortcut
#define Mumble  if (verbose) printf  
#define NEXT(u) ((u)->next ? (u)->next : user0)  // quickie linked list
#define True  1
#define False 0

// for broadcast

#define All 0
#define ActiveOnly 1


int clients    = 0;       // client counter
int is_daemon  = False;       // server running as daemon flag
int verbose    = True;       // how much blathering to do


struct user {                            // user record
  int           fd;                      // network file descriptor
  unsigned char nick[16];                // user nickname
  unsigned char number;                  // user number
  struct        user *next, *nextvictim; // linked list...
  char          active;                  // user aliveness
  char          introduced;              // user finished initiation right
  unsigned char readbuf[BUFSIZE];        // io buffers & counters
  int           nread;
  unsigned char writbuf[BUFSIZE];
  int           nwrite;
};


struct user        *user0 = NULL;        // initial user list
int                port   = DEFAULTPORT; // port number
struct sockaddr_in saddr;                // socket address and type
int                lfd;                  // file descriptor of socket
unsigned char      realbuf[512];         // ??
unsigned char      *buf = realbuf + 4;   // ??
int                interrupt;            // loop interruption
int                tcp = -1;             // 
fd_set             fds;                  // set of file descriptors


void fatal(char *s) ;
void syserr(char *s) ;
void addtobuffer(struct user *u, unsigned char *b, int len) ;
void flushuser(struct user *u) ;
void broadcast(struct user *except, int len, int activeonly) ;
void sendtoone(struct user *to, int len) ;
void dropuser(struct user *u) ;
void new_connect(int fd) ;
void DoCommand (struct user *u);


int main(int argc, char *argv[]) {
  int             i;                 // general index counter
  int             sl;                // select result
  int             on;                // used as network option flag
  struct user     *u;                // user itterator
  int             mfd;               // 
  int             r;                 // read character count 
  struct protoent *tcpproto;         // tcp protocol descriptor
  struct timeval  tv;                // timeout for network messages

#ifndef NeXT
  struct sigaction sact;
#endif

#ifdef NeXT
  signal(SIGPIPE, SIG_IGN);
#else
  sact.sa_handler = SIG_IGN;
  sigemptyset(&sact.sa_mask);
  sact.sa_flags = 0;
  sigaction(SIGPIPE, &sact, NULL);
#endif

  // lookup the tcp protocol
  if ((tcpproto = getprotobyname("tcp")) != NULL) {
    tcp = tcpproto->p_proto;
  }

  // establish a socket
  if ((lfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    syserr("socket");
  }
  
  // set up the socket  
  on = 1;
  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)); 
  
  // bind socket details to the socket
  saddr.sin_family      = AF_INET;
  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  saddr.sin_port        = htons(port);
  
  if (bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
    syserr("bind");
  }
  
  // start listening  
  listen(lfd, 5);

  // daemonize if requested
  if (is_daemon) {
    /* become a daemon, breaking all ties with the controlling terminal */
    verbose = 0;
    for (i = 0; i < 255; i++) {
      if (i != lfd)
	     close(i);
    }
    if (fork())
      exit(0);
    setsid();
    if (fork())
      exit(0);
    chdir("/");
    /* open a fake stdin, stdout, stderr, just in case */
    open("/dev/null", O_RDONLY);
    open("/dev/null", O_WRONLY);
    open("/dev/null", O_WRONLY);
  }

  Mumble("server started. Listening on port %d\n", port);

  // ----------------------- primary loup ------------------------
  while(1) {
    interrupt = False;

    for (u = user0; u; u = u->next) {
      flushuser(u);
    }

    FD_ZERO(&fds); //clear the set of fd's
    mfd = lfd;
    u = user0;  
    while (u) {
      FD_SET(u->fd, &fds); // add fd to fds
      if (u->fd > mfd)
	      mfd = u->fd;
      u = u->next;
    }
       
    FD_SET(lfd, &fds);
    tv.tv_sec  = 0;
    tv.tv_usec = 500000;
    
    if ((sl = select(mfd + 1, &fds, NULL, NULL, &tv)) < 0) { // has anyone said anything?
      if (errno != EINTR) { // A non blocked signal was caught.
	      syserr("select");
      } else {
         continue;
      }
    }

    if (!(sl > 0)) // < or == (error or nothing happened)
      continue;

    if (FD_ISSET(lfd, &fds)) { // find out if lfd is in fds
      int newfd, slen;

      slen = sizeof(saddr);
     
      newfd = accept(lfd, (struct sockaddr *)&saddr, &slen);
      if (newfd < 0) {
	      if (errno != EINTR) {
	        syserr("accept");
         }
      } else {
	     if (tcp != -1) {
	       on = 1;
	       setsockopt(newfd, tcp, TCP_NODELAY, (char *)&on, sizeof(int));
	     }
        
	     new_connect(newfd); // initiate user
                
      }
     continue;
     }

    u = user0;
    
    // Read and interpert from each client
    
    do {
      if (FD_ISSET(u->fd, &fds)) {
         // read characters from the client
	      r = read(u->fd, u->readbuf + u->nread, BUFSIZE - u->nread);
          
	      if (r <= 0) { // handle EOF from client
	        Mumble("EOF from client %d (%s)\n", u->number, u->nick);
	        dropuser(u);
	        interrupt = True;
	        break;
	      }
	      u->nread += r; // update buffer counter
         
         Mumble("Client %d sends %d characters. %d total\n", u->number, r, u->nread);
         
	      while ( u->nread >= 0 ) {
         
           DoCommand( u );
           
	      }
      }
      
      if (u && (!interrupt))
	     u = u->next;    
    } while (u && (!interrupt));
    
  } // end of while(1)
} // end of main



void DoCommand (struct user *u) {

  buf[0] = *(u->readbuf+u->nread-1);
  u->nread--; 
  Mumble("Broadcasting character %c\n", buf[0]); 
  broadcast(u, 1, All);
  return;
}




////////////ickg


void fatal(char *s) {
  if (!is_daemon)
    fprintf(stderr, "%s.\n", s);
  exit(1);
}

void syserr(char *s) {
  if (!is_daemon)
    fprintf(stderr, "fatal: %s failed.\n", s);
  exit(1);
}

void addtobuffer(struct user *u, unsigned char *b, int len) {
  if (u->nwrite + len >= BUFSIZE)
    fatal("Internal error: send buffer overflow");
  memcpy(u->writbuf + u->nwrite, b, len);
  u->nwrite += len;
}

void flushuser(struct user *u) {
  if (u->nwrite) {
    write(u->fd, u->writbuf, u->nwrite);
    u->nwrite = 0;
  }
}

void broadcast(struct user *except, int len, int activeonly) {
  struct user *u;
/*
  realbuf[0] = realbuf[1] = realbuf[2] = realbuf[3]= ' ';
  for (u=user0; u; u=u->next)
    if (u != except && (u->active || !activeonly) && u->introduced)
      addtobuffer(u, realbuf, 4 + len);
*/    

  for (u=user0; u; u=u->next)
    if (u != except && (u->active || !activeonly) && u->introduced)
      addtobuffer(u, buf, len);

      
}

void sendtoone(struct user *to, int len) {
/*
  realbuf[0] = realbuf[1] = realbuf[2] = realbuf[3]= ' ';
  addtobuffer(to, realbuf, 4 + len);
*/

  realbuf[0] = realbuf[1] = realbuf[2] = realbuf[3]= ' ';
  addtobuffer(to, buf, len);

}

void dropuser(struct user *u) {
  struct user *v, *w;
  
  Mumble("dropping client %d (%s)\n", u->number, u->nick);
  if (u == user0)
    user0 = u->next;
  else {
    for (v=user0; v; v=v->next)
      if (v->next && v->next == u) {
	v->next = u->next;
	break;
      }
  }
  close(u->fd);

  if (u->introduced) {
    buf[0] = 'B';
    broadcast(u, 1, All);
  }

  for (v=user0; v; v=v->next) {
    if (v->nextvictim == u) {
      for (w=NEXT(v); w!=v; w=NEXT(w)) {
	if (w->active /*&& w->playing !!!???!!!*/) {
	  v->nextvictim = w;
	  break;
	}
      }
      if (v->nextvictim == u)
	v->nextvictim = NULL;
    }
  }

  free(u);
  clients--;

}


void new_connect(int fd) {
  struct user *u, *v;
  unsigned char nxn;

   write(fd, "\nWelcome, now go away.\n", 24);

  u = malloc(sizeof (struct user));
  if (!u)
    fatal("Out of memory");
  u->fd         = fd;
  u->nick[0]    = 0;
  u->next       = user0;
  u->nextvictim = NULL;
  u->active     = False;
  u->nread      = 0;
  u->nwrite     = 0;
  u->introduced = True;
  user0         = u;

  nxn = 1;
again:
  v = u->next;
  while(v) {
    if (v->number == nxn) {
      nxn++;
      goto again;
    }
    v = v->next;
  }
  u->number = nxn;
  Mumble("client %d connecting from %s\n", nxn, inet_ntoa(saddr.sin_addr));
  clients++;
  buf[0] = 'H';
  sendtoone(u, 1);
}






//EOC


/*

	  switch(buf[1]) {
	    case OP_NICK:
	      if (len>16)
		len=16;
	      memcpy(u->nick, &buf[2], len-2);
	      u->nick[len-2] = 0;
	      for (i=0; i<len-2; i++) {
		if (u->nick[i] < ' ' || 
		    (u->nick[i] > 0x7e && u->nick[i] <= 0xa0)) {
		  u->nick[i] = 0;
		  break;
		}
	      }

	      if (!u->introduced) {
		buf[0] = u->number;
		buf[1] = OP_NEW;
		broadcast(u, 2, 0);
	      }
	      
	      if (verbose)
		printf("client %d calls itself \"%s\"\n", u->number, u->nick);
	      buf[1] = OP_NICK;
	      broadcast(u, len, 0);

	      if (!u->introduced) {
		for (v=user0; v; v=v->next) {
		  if (v != u && v->introduced) {
		    buf[0] = v->number;
		    buf[1] = OP_NEW;
		    buf[2] = (v->games >> 8);
		    buf[3] = (v->games & 0xff);
		    sendtoone(u, 4);
		    buf[1] = OP_NICK;
		    memcpy(&buf[2], v->nick, 14);
		    sendtoone(u, 2+strlen(v->nick));
		  }
		}
		if (level != 5) {
		  buf[0] = 0;
		  buf[1] = OP_LEVEL;
		  buf[2] = level;
		  sendtoone(u, 3);
		}
		if (mode >= 0) {
		  buf[1] = OP_MODE;
		  buf[2] = mode;
		  sendtoone(u, 3);
		}
	      }

	      u->introduced = 1;
	      break;

	    case OP_KILL:
	      for (v=user0; v; v=v->next) {
		if (v->number == buf[2])
		  break;
	      }
	      if (v) {
		if (v->isbot) {
		  if (verbose)
		    printf("client %d (%s) kills bot %d (%s)\n", u->number, u->nick, v->number, v->nick);
		  dropuser(v);
		  interrupt = 1;
		  break;
		} else {
		  if (verbose)
		    printf("client %d (%s) attempting to kill non-bot %d (%s)\n", u->number, u->nick, v->number, v->nick);
		}
	      }
	      break;

	    case OP_PLAY:
	      if (verbose)
		printf("client %d (%s) starts game\n", u->number, u->nick);
	      timetoplay = 0;
	      do_play();
	      break;
	    
	    case OP_MODE:
	      mode = buf[2];
	      if (verbose)
		printf("client %d (%s) sets mode %d (%s)\n", u->number, u->nick, buf[2], buf[2] == 0 ? "normal" : (buf[2] == 1 ? "fun" : "unknown"));
	      broadcast(NULL, 3, 0);
	      break;

	    case OP_PAUSE:
	      if (verbose)
		printf("client %d (%s) pauses game\n", u->number, u->nick);
	      broadcast(NULL, 2, 0);
	      paused = 1;
	      break;

	    case OP_CONT:
	      if (verbose)
		printf("client %d (%s) continues game\n", u->number, u->nick);
	      broadcast(NULL, 2, 0);
	      paused = 0;
	      break;

	    case OP_BOT:
	      if (!u->isbot)
		bots++;
	      u->isbot = 1;
	      if (verbose)
		printf("client %d (%s) declares itself to be a bot\n", u->number, u->nick);
	      break;
	    
	    case OP_LEVEL:
	      level = buf[2];
	      if (verbose)
		printf("client %d (%s) sets level %d\n", u->number, u->nick, buf[2]);
	      broadcast(NULL, 3, 0);
	      break;

	    case OP_LOST:
	      {
		struct user *won = NULL;

		if (verbose)
		  printf("client %d (%s) has lost\n", u->number, u->nick);
		u->playing = 0;
		broadcast(u, 2, 1);
		i = 0;
		for (v=user0; v; v=v->next) {
		  if (v->nextvictim == u) {
		    for (w=NEXT(v); w!=v; w=NEXT(w)) {
		      if (w->active && w->playing) {
			v->nextvictim = w;
			break;
		      }
		    }
		    if (v->nextvictim == u)
		      v->nextvictim = NULL;
		  }
		}
		for (v=user0; v; v=v->next) {
		  if (v->playing) {
		    i++;
		    won = v;
		  }
		}
		if (i == 1) {
		  buf[0] = won->number;
		  buf[1] = OP_WON;
		  won->games++;
		  broadcast(NULL, 2, 0);
		} else if (i == 0) {
		  buf[0] = u->number;
		  buf[1] = OP_WON;
		  u->games++;
		  broadcast(NULL, 2, 0);
		}
		if (i < 2 && clients > 1 && !norestart)
		  timetoplay = time(NULL) + RESTARTDELAY;
	      }
	      break;
	    
	    case OP_ZERO:
	      broadcast(NULL, 2, 0);
	      if (verbose)
		printf("client %d (%s) resets the game counters\n", u->number, u->nick);
	      for (v=user0; v; v=v->next)
		v->games = 0;
	      break;

	    case OP_CLEAR:
	    case OP_GROW:
	      broadcast(u, 2, 1);
	      break;

	    case OP_MSG:
	      buf[len] = 0;
	      if (verbose)
		printf("client %d (%s) sends message: %s\n", u->number, u->nick, &buf[2]);
	      broadcast(u, len, 0);
	      break;

	    case OP_DRAW:
	      broadcast(u, len, 1);
	      break;

	    case OP_FALL:
	      broadcast(u, len, 1);
	      u->lines += len - 2;
	      if (!nospeedup && u->lines > level * 10 && level < 9) {
		level++;
		buf[0] = 0;
		buf[1] = OP_LEVEL;
		buf[2] = level;
		if (verbose)
		  printf("increasing the speed to level %d\n", level);
		broadcast(NULL, 3, 0);
	      }
	      break;
	    
	    case OP_VERSION:
	      if (len != 5 || buf[2] != PROT_VERS_1 || buf[3] != PROT_VERS_2) {
		if (verbose)
		  printf("client %d (%s) has wrong protocol version %d.%d.%d\n", u->number, u->nick, buf[2], buf[3], buf[4]);
		buf[0] = 0;
		buf[1] = OP_BADVERS;
		buf[2] = PROT_VERS_1;
		buf[3] = PROT_VERS_2;
		buf[4] = PROT_VERS_3;
		sendtoone(u, 5);
		flushuser(u);
		dropuser(u);
		interrupt = 1;
	      } else {
		if (verbose)
		  printf("client %d (%s) uses protocol version %d.%d.%d\n", u->number, u->nick, buf[2], buf[3], buf[4]);
	      }
	      break;

	    case OP_LINES:
	      if (len != 3) {
		if (verbose)
		  printf("client %d (%s) sends crap for an OP_LINES\n", u->number, u->nick);
		dropuser(u);
		interrupt = 1;
		break;
	      }
	      if (u->nextvictim) {
		if (verbose)
		  printf("client %d (%s) sends %d %s to client %d (%s)\n", u->number, u->nick, (int)buf[2], buf[2] == 1 ? "line" : "lines", u->nextvictim->number, u->nextvictim->nick);
		sendtoone(u->nextvictim, 3);
		buf[3] = u->nextvictim->number;
		buf[1] = OP_LINESTO;
		broadcast(u->nextvictim, 4, 1);
		for (v=NEXT(u->nextvictim); v!=u->nextvictim; v=NEXT(v)) {
		  if (v->active && v != u && v->playing) {
		    u->nextvictim = v;
		    break;
		  }
		}
	      } else if (verbose)
		printf("client %d (%s) makes %d %s but has no victim\n", u->number, u->nick, (int)buf[2], buf[2] == 1 ? "line" : "lines");
	      break;
	    
	    default:
	      if (verbose)
		printf("opcode %d from client %d (%s) not understood\n", buf[0], u->number, u->nick);
	  }
     
     */
