/*
 * Atom-4 network server
 * Header file
 *
 * $Id: server.h,v 1.12 2003/04/15 20:39:28 hsteoh Exp hsteoh $
 * --------------------------------------------------------------------------
 * PROTOCOL
 *
 * Please see protocol.txt.
 */

#ifndef SERVER_H
#define SERVER_H

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "event.h"
#include "game.h"
#include "net.h"


#define MAX_CLIENTS		12	// NOTE: must be < FD_SETSIZE
#define SERVER_BACKLOG		2	// socket backlog limit


class clientconn : public netconn {
  friend class server;

  server *serv;				// [R]
  int id;				// client ID as registered in *serv
  char *hostname;			// [O]
  netparser parser;			// packet parser

  enum state_t {			// connection state
    HANDSHAKE=1,			// initial handshake
    CONNECTED=3,			// connected
    DISCONN=10				// to be disconnected
  } connstate;

  int playnum;				// 1 or 2 if playing, 0 if watching

  // Protocol dispatch table
  static struct dispatch {
    char *packet_type;
    void (clientconn::*handler)();
  } dispatch_tbl[];

  // Dispatch functions
  void p_name();
  void p_requ();
  void p_move();
  void p_rsgn();
  void p_chat();
  void p_next();
  void p_play();
  void p_watch();
  void p_quit();
  void p_who();

  // Convenience functions
  void fatal_error(char *fmt, ...);
  void not_implemented();
  int next_number();			// parse next word in parser as number

  // Processors for incoming packets
  void handshake();
  void enter_connected_state();		// initial stuff to be done when client
					// first enters connected state
  void connected_state();

  // Base class pure virtuals
  void process_packet(char *packet);
  void disconnected();
public:
  // ([O])
  clientconn(server *s, int cid, int sock, char *hostname, eventloop *loop,
             int sendlimit);
  ~clientconn();

  void send_board(board4 *b);
  int plnum() { return playnum; }
  void round_over();
};

class server {
public:
  enum opt_t {				// can be OR'd together
    LOCAL_ONLY=1,			// listen only on loopback address
    ALLOW_OBSERVERS=2			// allow clients to connect to watch,
					// but not play. (TBD)
  };
private:
  eventloop *loop;			// [R]

  int options;				// technically, many OR'd opt_t's
  inline int opt(opt_t flag) { return options & flag; }

  // Network-specific stuff
  int port;				// listen port
  int sock;				// listen sock
  struct sockaddr_in sockname;		// socket address

  class serversock : public eventhandler {
    server *serv;			// server object
  public:
    serversock(server *s) : serv(s) {}
    void read_ready(eventloop *src, int fd);
    void write_ready(eventloop *src, int fd);
  } listener;

  // Client disconnector
  class disconnector : public timerhandler {
    server *serv;
    int cid;
  public:
    disconnector(server *s, int client_id) : timerhandler(DYNAMIC), serv(s),
	cid(client_id) {}
    void tick(eventloop *src, timeval t);
  };

  fd_set csocks;			// client sockets
  int csocks_max;			// max fd's in csocks
  clientconn *clients[MAX_CLIENTS];	// [O]

  // Game-specific stuff
  atom4 *game;				// [R] game engine
  clientconn *players[NUM_PLAYERS];	// [R]

  int create_servsock(int port, struct sockaddr_in *sockname, int backlog);
  int new_client();
  void broadcast(char *fmt, ...);	// broadcast message to all clients
  void reset_players();
public:
  server(atom4 *engine, eventloop *loop, int port, int options);
  ~server();

  void disconnect_client(int cid);

  // Requests for global information
  void send_names(int cid);

  // Game flow functions
  // - need_players() returns non-zero if there are still slots available for
  //   joining the current game
  // - assign_player() assigns the client to a player number if available, and
  //   returns the player number; otherwise returns 0 (watcher).
  // - move() makes a move. The following codes are returned:
  //	1	Move successful
  //	0	Illegal move
  //	-1	Game not started yet
  //	-2	Not the player's turn to move
  int need_players();			// are there still playing slots left?
  int assign_player(int cid);
  int move(int player, int x, int y);

  board4 *get_board() { return game->get_board(); }
};

#endif // SERVER_H
