/*

 */

#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

int clients    = 0;       // client counter
int bots       = 0;       
int norestart  = 0;
int onceonly   = 0;
int timetoplay = 0;
int is_daemon  = 0;       // server running as daemon flag
int verbose    = 1;       // how much blathering to do
int level      = 5;
int mode       = -1;
int paused     = 0;
int nospeedup  = 0;



struct user {                        // user record
  int fd;                            // network file descriptor
  unsigned char nick[16];            
  unsigned char number;
  struct user *next, *nextvictim;
  char active;
  char introduced;
  unsigned char readbuf[BUFSIZE];   // io buffers
  int nread;
  unsigned char writbuf[BUFSIZE];
  int nwrite;
  int lines;
  unsigned int games;
};

struct user *user0 = NULL;           // initial user list

#define NEXT(u) ((u)->next ? (u)->next : user0)  // quickie linked list

int                port = DEFAULTPORT;
struct sockaddr_in saddr;
int                lfd;
unsigned char      realbuf[512];
unsigned char      *buf = realbuf + 4;

int                interrupt;
int                tcp = -1;

fd_set             fds;

void copydown(char *s, char *t, int n) ;
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 do_play() ;
void new_connect(int fd) ;

int main(int argc, char *argv[]) {
  int             i, sl, on;
  struct user     *u, *v, *w;
  int             mfd;
  int             r; 
  unsigned int    len;
  char            *opt;
  struct protoent *tcpproto;
  struct timeval  tv;

#ifndef NeXT
  struct sigaction sact;
#endif

/*
getprotobyname()
RETURN VALUE
       The getprotoent(), getprotobyname() and getprotobynumber()
       functions return the protoent structure, or a NULL pointer
       if an error occurs or the end of the file is reached.
       
       struct protoent {
          char    *p_name;        // official protocol name 
          char    **p_aliases;    // alias list 
          int     p_proto;        // protocol number 
       }

*/

  if ((tcpproto = getprotobyname("tcp")) != NULL) 
    tcp = tcpproto->p_proto;

#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

/*
int socket(int domain, int type, int protocol);
RETURN VALUE
       -1 is returned if an error occurs;  otherwise  the  return
       value is a descriptor referencing the socket.

 PF_INET            
      IPv4 Internet protocols          ip(7)
 SOCK_STREAM
      Provides sequenced, reliable, two-way,  connection-
      based  byte streams. 
*/

  lfd = socket(PF_INET, SOCK_STREAM, 0);
  
  /*
   #define AF_INET          2       // Internet IP Protocol      
  */
  saddr.sin_family      = AF_INET;
  
  /*
     The htonl() function converts the  long  integer  hostlong
       from host byte order to network byte order.
       
   // Address to accept any incoming messages.
   #define      INADDR_ANY              ((unsigned long int) 0x00000000)
   
  */
  
  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  
  /*
    The  htons() function converts the short integer hostshort
       from host byte order to network byte order.
  */
  
  saddr.sin_port        = htons(port);

  if (lfd < 0)
    syserr("socket");
  on = 1;

/*
int setsockopt(int s, int level, int optname,  const  void
       *optval, socklen_t optlen);
 To manipulate options at the socket level, level is
       specified  as  SOL_SOCKET.
 #define SO_REUSEADDR    2 

*/

  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int));
  
  /*
  int  bind(int  sockfd, struct sockaddr *my_addr, socklen_t
       addrlen);
       
    bind gives the socket sockfd the  local  address  my_addr.
       my_addr  is  addrlen  bytes  long.  Traditionally, this is
       called "assigning a name to a socket."  When a  socket  is
       created with socket(2), it exists in a name space (address
       family) but has no name assigned.

       It is normally necessary to assign a local  address  using
       bind  before  a SOCK_STREAM socket may receive connections
       (see accept(2)).
             
  RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned,
       and errno is set appropriately.  
  */
  
  if (bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    syserr("bind");

  /*
    int listen(int s, int backlog);
  DESCRIPTION
       To  accept  connections,  a  socket  is first created with
       socket(2), a willingness to  accept  incoming  connections
       and  a  queue limit for incoming connections are specified
       with listen, and then the connections  are  accepted  with
       accept(2).   The  listen  call  applies only to sockets of
       type SOCK_STREAM or SOCK_SEQPACKET.

       The backlog parameter defines the maximum length the queue
       of  pending  connections  may  grow  to.   If a connection
       request arrives with the queue full the client may receive
       an  error  with  an  indication of ECONNREFUSED or, if the
       underlying protocol supports retransmission,  the  request
       may be ignored so that retries succeed.
  RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned,
       and errno is set appropriately.

  */

  listen(lfd, 5);

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

  if (verbose)
    printf("server started. Listening on port %d\n", port);

  while(1) {
    interrupt = 0;

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

    /*
        FD_ZERO will clear a set.
    */

    FD_ZERO(&fds);
    mfd = lfd;
    u = user0;  
    while (u) {
    /*
     FD_SET and FD_CLR add or remove a given
       descriptor from  a  set.
    */
      FD_SET(u->fd, &fds);
      if (u->fd > mfd)
	      mfd = u->fd;
      u = u->next;
    }
       
    FD_SET(lfd, &fds);
    tv.tv_sec  = 0;
    tv.tv_usec = 500000;
    
    /*
    int  select(int  n,
                fd_set  *readfds,
                fd_set  *writefds,
                fd_set  *exceptfds,
                struct timeval *timeout);

     On  success,  select  and  pselect  return  the  number of
       descriptors contained in the descriptor sets, which may be
       zero  if  the  timeout expires before anything interesting
       happens.  On error, -1  is  returned,  and  errno  is  set
       appropriately;  the  sets and timeout become undefined, so
       do not rely on their contents after an error.
    */
    
    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)  // error
      continue;

/*
    if (have to leave now) {
      if (verbose)
	      printf("shutting down\n");
      while (user0)
	      dropuser(user0);
      continue;
    }
  
*/    
    if (sl == 0) //nothing happened?
      continue;
/*
FD_ISSET  tests  to  see  if  a
  descriptor is part of the set
*/
    if (FD_ISSET(lfd, &fds)) {
      int newfd, slen;

      slen = sizeof(saddr);
      
      /*
      int   accept(int   s,  struct  sockaddr  *addr,  socklen_t
       *addrlen);
       
     The accept function is used with  connection-based  socket
       types  (SOCK_STREAM,  SOCK_SEQPACKET  and  SOCK_RDM).   It
       extracts the first connection  request  on  the  queue  of
       pending  connections,  creates a new connected socket with
       mostly the same properties as s, and allocates a new  file
       descriptor  for  the socket, which is returned.  The newly
       created socket is no longer in the listening  state.
       
      RETURN VALUE
       accept returns -1 on error.  If it succeeds, it  returns
       a  non-negative  integer  that  is  a  descriptor  for the
       accepted socket.
      */
      
      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); //  !!!???!!!
        /*  ssize_t write(int fd, const void *buf, size_t count); */
        write(newfd, "\nWelcome, now go away.\n", 24);
        
      }
     continue;
     }

    u = user0;
    
    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
	        if (verbose)
	           printf("EOF from client %d (%s)\n", u->number, u->nick);
	        dropuser(u);
	        interrupt = 1;
	        break;
	      }
	      u->nread += r; // update buffer counter
         
         
	      while ( /*buffer_valid*/ 0 ) {
         
           // DoCommand()
     
	      }
      }
      
      if (u && (!interrupt))
	     u = u->next;
    } while (u && (!interrupt));
    
  } // end of while(1)
} // end of main








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






/* like memcpy, but guaranteed to handle overlap when s <= t */
void copydown(char *s, char *t, int n) {
  for (; n; n--)
    *(s++) = *(t++);
}

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] = 0;
  realbuf[3] = (unsigned char)len;
  for (u=user0; u; u=u->next)
    if (u != except && (u->active || !activeonly) && u->introduced)
      addtobuffer(u, realbuf, 4 + len);
}

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

void dropuser(struct user *u) {
  struct user *v, *w;
  
  if (verbose)
    printf("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] = u->number;
    buf[1] = 0; //OP_GONE; !!!???!!!
    broadcast(u, 2, 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;
    }
  }

/*
  if (u->isbot)
    bots--;
*/

  free(u);
  clients--;

  if (onceonly && clients == bots) {
    if (verbose)
      printf("no human clients left: exiting\n");
    exit(0);
  }

  if (clients == 0) {
    mode = -1;
    level = 5;
    timetoplay = 0;
  }
}

void do_play() {
  struct user *v, *w;

  for (w=user0; w; w=w->next) {
    if (w->introduced) {
      w->active = 1;
    /*  w->playing = 1; */
      w->lines = 0;
      w->nextvictim = NULL;
      for (v=NEXT(w); v!=w; v=NEXT(v)) {
	if (v->introduced) {
	  w->nextvictim = v;
	  break;
	}
      }
    }
  }
  if (paused) {
    paused = 0;
  /*  buf[1] = OP_CONT; !!!???!!! */
    broadcast(NULL, 2, 0);
  }
  /* buf[1] = OP_PLAY; !!!???!!! */
  broadcast(NULL, 2, 0);
}

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

  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 = 0;
  u->nread = 0;
  u->nwrite = 0;
 /* u->playing = 0; !!!???!!! */
 /* u->isbot = 0; !!!???!!! */
  u->introduced = 0;
  u->games = 0;
  user0 = u;

  nxn = 1;
again:
  v = u->next;
  while(v) {
    if (v->number == nxn) {
      nxn++;
      goto again;
    }
    v = v->next;
  }
  u->number = nxn;
  if (verbose)
    printf("client %d connecting from %s\n", nxn, inet_ntoa(saddr.sin_addr));
  clients++;
  /* buf[1] = OP_YOUARE; !!!???!!!*/
  buf[0] = u->number;
  sendtoone(u, 2);
}



/*

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