/* This file is part of cardwords
   (c) 1999 Tobias Peters
   see file COPYING for the copyright terms.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
// cardwords_server.hh

#ifndef CARDWORDS_SERVER_HH
#define CARDWORDS_SERVER_HH

#include "cardwords_selectiblechild.hh"
#include "cardwords_bonustable.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifndef SERVER_VERSION
#define SERVER_VERSION "cardwords_server version 0.0a"
#endif

#ifndef DICBOT_EXECUTABLE_FILE
#define DICBOT_EXECUTABLE_FILE "cardwords_dicbot"
#endif
#ifndef DICBOT_TIMEOUT
#define DICBOT_TIMEOUT 60 /* seconds */
#endif

#ifndef OWNER_PASSWORD_LENGTH
#define OWNER_PASSWORD_LENGTH 10
#endif
#ifndef PORT_NUMBER
#define PORT_NUMBER DEFAULT_PORT
#endif
#ifndef NETWORK_TIMEOUT
#define NETWORK_TIMEOUT 30
#endif

#ifndef MESSAGE_NUMBER_DIGITS
#define MESSAGE_NUMBER_DIGITS 8
#endif
#ifndef MESSAGE_LENGTH_DIGITS
#define MESSAGE_LENGTH_DIGITS (MESSAGE_NUMBER_DIGITS)
#endif
#ifndef MESSAGE_HEADER_LENGTH
#define MESSAGE_HEADER_LENGTH (MESSAGE_NUMBER_DIGITS+MESSAGE_LENGTH_DIGITS+1)
#endif
#ifndef MESSAGE_ANSWER_INDICATOR
#define MESSAGE_ANSWER_INDICATOR ('A')
#endif
#ifndef MESSAGE_INFO_INDICATOR
#define MESSAGE_INFO_INDICATOR ('I')
#endif
#ifndef MESSAGE_PERSONAL_INFO_INDICATOR
#define MESSAGE_PERSONAL_INFO_INDICATOR ('P')
#endif
#ifndef MESSAGE_BYE
#define MESSAGE_BYE "BYE"
#endif
#ifndef MESSAGE_2ND_OWNER_TRIED
// This message will be sent to the owner when a second attempt to log in as
// owner is made:
#define MESSAGE_2ND_OWNER_TRIED "2ND_OWNER_TRIED"
#endif
#ifndef MESSAGE_LOG_AS
// This message is the permission to log on. It will be followed by at least
// one of STRING_PLAYER, STRING_WATCHER, STRING_OWNER_PLAYER,
// STRING_OWNER_WATCHER, separated by white space.
#define MESSAGE_LOG_AS "LOG_AS"
#endif
#ifndef MESSAGE_LOG_AS_END
// And after this char the server will send:
// The Charset description, the card descriptions, the bonus descriptions,
// the table description.
#define MESSAGE_LOG_AS_END (';')
#endif
#ifndef MESSAGE_NON_UNIQUE_NAME
#define MESSAGE_NON_UNIQUE_NAME "NON_UNIQUE_NAME"
#endif
#ifndef MESSAGE_FULL
#define MESSAGE_FULL "FULL"
#endif
#ifndef MESSAGE_WELCOME
#define MESSAGE_WELCOME "WELCOME"
#endif
#ifndef MESSAGE_GAME_ALREADY_RUNNING
#define MESSAGE_GAME_ALREADY_RUNNING \
                        "GAME_ALREADY_RUNNING"
#endif
#ifndef MESSAGE_NEW_CARD
#define MESSAGE_NEW_CARD "NEW_CARD"
#endif
#ifndef MESSAGE_NOT_OWNER
#define MESSAGE_NOT_OWNER "NOT_OWNER"
#endif
#ifndef MESSAGE_GAME_STARTS
#define MESSAGE_GAME_STARTS "GAME_STARTS"
#endif
#ifndef MESSAGE_GAME_ENDS
#define MESSAGE_GAME_ENDS "GAME_ENDS"
#endif
#ifndef MESSAGE_GO_OF // It's xxx's go ...
#define MESSAGE_GO_OF "GO_OF"
#endif
#ifndef MESSAGE_YOUR_GO
#define MESSAGE_YOUR_GO "YOUR_GO"
#endif
#ifndef MESSAGE_GO_FINISHED
#define MESSAGE_GO_FINISHED "GO_FINISHED"
#endif

#ifndef MESSAGE_LOGGED_PLAYERS // This message is followed by:
//                               - the number of robot players
//                                 and STRING_ROBOTS
//                               - the names and numbers of cards and the
//                                 points of each robot
//                               - the number of remote players
//                                 and STRING_REMOTE_PLAYERS
//                               - the names and numbers of cards and the
//                                 points of each remote player
#define MESSAGE_LOGGED_PLAYERS "LOGGED_PLAYERS"
#endif

#ifndef MESSAGE_REMAINING_CARDS // The number of cards in the pile
#define MESSAGE_REMAINING_CARDS "REMAINING_CARDS"
#endif

#ifndef MESSAGE_PLAYER_TRIES_MOVE // This message is folowed by:
//                                  - the name of the player who tries the
//                                    move
//                                  - the move that he tries.
#define MESSAGE_PLAYER_TRIES_MOVE "PLAYER_TRIES_MOVE"
#endif

#ifndef MESSAGE_PLAYER_GETS_POINTS
#define MESSAGE_PLAYER_GETS_POINTS \
                          "MESSAGE_PLAYER_GETS_POINTS"
#endif

#ifndef MESSAGE_MOVE_DONE
#define MESSAGE_MOVE_DONE "MESSAGE_MOVE_DONE"
#endif

#ifndef MESSAGE_PLAYER_PASSES // This message is folowed by the name of the
#define MESSAGE_PLAYER_PASSES "MESSAGE_PLAYER_PASSES"
#endif                        // passing player.

#ifndef MESSAGE_MOVE_NOT_ALLOWED
#define MESSAGE_MOVE_NOT_ALLOWED "MOVE_NOT_ALLOWED"
#endif

#ifndef MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD
#define MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD \
"MOVE__WOULD_CREATE_UNKNOWN_WORD"
#endif // Two underscores^ to distinguish it from the next one easily.

#ifndef MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD_AT
#define MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD_AT \
"MOVE_WOULD_CREATE_UNKNOWN_WORD_AT"
#endif

#ifndef MESSAGE_PLAYER_MUST_PASS // As a reaction to an invalid move or
//                                  unknown word
#define MESSAGE_PLAYER_MUST_PASS "PLAYER_MUST_PASS"
#endif


#ifndef MESSAGE_ACCEPTING_YOUR_TRADE
#define MESSAGE_ACCEPTING_YOUR_TRADE \
                             "ACCEPTING_YOUR_TRADE"
#endif

#ifndef MESSAGE_NO_SUCH_CARD // Personal message in response to a move or
//       trade request that had a card that is not on the player's hand.
#define MESSAGE_NO_SUCH_CARD "NO_SUCH_CARD"
#endif

#ifndef MESSAGE_NEW_WORD // Notify all clients when the owner adds a word
#define MESSAGE_NEW_WORD "NEW_WORD"
#endif

#ifndef MESSAGE_WORD_DELETED // as above, but word was deleted
#define MESSAGE_WORD_DELETED "WORD_DELETED"
#endif

#ifndef MESSAGE_PLAYER_TRADES_CARDS
#define MESSAGE_PLAYER_TRADES_CARDS "PLAYER_TRADES_CARDS"
#endif

#ifndef STRING_OWNER_PLAYER
#define STRING_OWNER_PLAYER "OWNER_PLAYER"
#endif
#ifndef STRING_PLAYER
#define STRING_PLAYER "PLAYER"
#endif
#ifndef STRING_WATCHER
#define STRING_WATCHER "WATCHER"
#endif
#ifndef STRING_OWNER_WATCHER
#define STRING_OWNER_WATCHER "OWNER_WATCHER"
#endif
#ifndef STRING_ROBOTS
#define STRING_ROBOTS "ROBOTS:"
#endif
#ifndef STRING_REMOTE_PLAYERS
#define STRING_REMOTE_PLAYERS "REMOTE_PLAYERS:"
#endif
#ifndef STRING_CARDS
#define STRING_CARDS "CARDS"
#endif
#ifndef STRING_POINTS
#define STRING_POINTS "POINTS"
#endif

#ifndef REQUEST_REGISTER
#define REQUEST_REGISTER "REGISTER"
#endif
#ifndef REQUEST_START
#define REQUEST_START "START"
#endif
#ifndef REQUEST_MOVE
#define REQUEST_MOVE "MOVE"
#endif
#ifndef REQUEST_TRADE
#define REQUEST_TRADE "TRADE"
#endif
#ifndef REQUEST_PASS
#define REQUEST_PASS "PASS"
#endif
#ifndef REQUEST_ADD_WORD
#define REQUEST_ADD_WORD "ADD_WORD"
#endif


#ifndef REQUEST_HEADER_DIGITS
#define REQUEST_HEADER_DIGITS (MESSAGE_NUMBER_DIGITS)
#endif
#ifndef REQUEST_MAX_LEN
#define REQUEST_MAX_LEN 10000
#endif
#ifndef REQUEST_MAX_NAME_LEN
#define REQUEST_MAX_NAME_LEN 30
#endif

class CardWords_Game;
class CardWords_CardTable;
class CardWords_Pile;

struct CardWords_Server_Global {
  string charset_file;
  string table_file;
  string card_file;
  string load_file;
  string home_directory;

  const string version;
  
  const CardWords_BonusTable * bonus_table;
  
  CardWords_Selector        selector;
  CardWords_SelectibleChild dicbot;

  // these will be specified via command line options:
  size_t no_of_players_planned;
  size_t no_of_robots_planned;
  size_t no_of_watchers_planned;
  int port_number;
  bool reserve_player_place_for_owner;
  char owner_password[OWNER_PASSWORD_LENGTH + 1]; // FIXME: Convert to string


  CardWords_Game * engin;
  CardWords_CardTable * table;
  CardWords_Pile * pile;
  
  CardWords_Server_Global() :
    version(SERVER_VERSION),
    bonus_table(0),
    dicbot(&selector, DICBOT_EXECUTABLE_FILE),
    no_of_players_planned(1),
    no_of_robots_planned(1),
    no_of_watchers_planned(0),
    
    port_number(PORT_NUMBER),
    reserve_player_place_for_owner(true),
    engin(0), table(0), pile(0)
    {
      // Produce the owner_password from /dev/urandom.
      // Yes, this is overkill.
      char random_bytes[(OWNER_PASSWORD_LENGTH + 1)/2];
      int fd = open("/dev/urandom",O_RDONLY);
      if (fd != -1) {
        read(fd, random_bytes, (OWNER_PASSWORD_LENGTH + 1)/2);
        close (fd);
      }
      for (size_t index = 0; index < (OWNER_PASSWORD_LENGTH + 1)/2; ++index) {
        owner_password[2*index] = 'A' + random_bytes[index] % 26;
        owner_password[2*index+1]= 'A' + random_bytes[index] / 26;
      }
      owner_password[OWNER_PASSWORD_LENGTH] = '\0';
    }
};

extern struct CardWords_Server_Global server_global;

#endif
