/* 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_participant.hh

#ifndef CARDWORDS_PARTICIPANT_HH
#define CARDWORDS_PARTICIPANT_HH

#include <assert.h>
#include <deque>
#include "cardwords_select.hh"
#include "cardwords_hand.hh"
#include "cardwords_exceptions.hh"
#include "cardwords_selectiblechild.hh"

class CardWords_Game;

#define INFO_MESSAGE (CardWords_Participant::info)
#define ANSWER_MESSAGE (CardWords_Participant::answer)
#define PERSONAL_MESSAGE (CardWords_Participant::personal_info)

class CardWords_Participant :
  public CardWords_Selectible,
  public CardWords_SelectibleChildLocker
{
private:
  // true for player, false for watcher:
  bool player;
public:
  bool is_player(void) const {return player;}
  void make_player(void);
  void make_watcher(void) {player = false;}
  bool is_watcher(void)const {return !player;}
private:
  // owner is also either player or watcher, so we need only an additional
  // bool if owner or not:
  bool owner;
public:
  bool is_owner(void) const {return owner;}
  void make_owner(void) {owner = true;}
  void unmake_owner (void);
  
private:
  // Evrey participant must have a name. If name is NULL, then the
  // participant is still in logging in phase:
  Machine_Char *name;
public:
  bool has_name (void) const {return name != 0;}
  const Machine_Char * get_name (void) const {return name;}
  void set_name (const Machine_Char * newname);
private:
  // Password is required for owner and perhaps also for players for
  // continuing a saved game.
  bool password_required;
  bool password_approved;
#if 0 
  char *password;
#endif
public:
#if 0 // that's overkill, leave it out:
  void set_password (const char *p) {
    assert (p != 0);
    // FIXME: no password changing ?
    assert (password == 0);
    delete (password);
    size_t length = strlen(p);
    if (length > MAX_NAME_LENGTH) {
      throw CardWords_XBufferAttack("set_password");
    }
    password = new char [length + 1];
    strcpy (password, p);
  }
  bool check_password (const char *p) const {
    assert (p != 0);
    assert (password != 0);
    return strcmp(p, password) == 0;
  }
#endif
  bool need_password (void) const { return password_required;}
#if 0 // again overkill
private:
  // overriding the password authorisation, should only be done by owner
  CardWords_Participant * overriding_password_of;
public:
  const CardWords_Participant *overriding_password_of_whom (void) const
    { return overriding_password_of; }
  void override_password (const CardWords_Participant * powner) {
    assert (password != 0);
    assert (powner != 0);
    if (powner->overriding_password_of_whom() != this
        || powner->is_owner() == false) {
      throw CardWords_XFakeOwnerAttack;
    }
    delete password;
    password = 0;
  }
#endif
private:
  // if this CardWords_Participant actually has a participant:
  // this may be false when a saved game is loaded again
  // and one of the players has not shown up yet; or if
  // this participant is a robot.
  bool remote_participant;
  bool robot; // only playing robots allowed, not watching ones.
public:
  bool is_remote_participant(void) const {
    assert(robot != remote_participant);
    return remote_participant;
  }
  bool is_robot(void) const {
    assert(robot != remote_participant);
    return robot;
  }
  
  // The communication section:
private:
  int fd; // the filedescriptor of the socket
  CardWords_Selector * selector;
  bool registered_in_read_mode;
  bool registered_in_write_mode;
  // The numbers of received and answerded requests and the like

  size_t received_requests;
  size_t answered_requests; // Should most of the time be equal to
  //          received_requests, is one less when working on the last request.
  size_t last_sent_info; // Everyone gets infos about the latest move and
  //                        the like without a request for it.
  size_t last_personal_info; // Contains the number of personal infos sent to
  //                            this participant
  bool   dicbot_query_pending; // True if we wait for an answer of dicbot.

  bool receiving_request; // True if we are receiving a remote request.
  bool receiving_header; // True if we are curently receiving a request
  //                        header.
  bool receiving_body; // True if we are curently receiving a request's body.
  size_t bytes_expected; // The number of bytes of the header/body that we
  //                        expect.
  size_t bytes_received; // The portion of bytes that have successfully been
  //                        received already.
  Machine_Char * request; // the pointer to the received request header/body
  
public:
  // A type to differnentiate between the three types of messages that can be
  // sent over to the participant's client:
  enum E_Message_Type {answer, info, personal_info};

  // Don't listen to remote requests any more:
  void block_reading (void); 

private:
  // Messages may need to be queued because there is currently another message
  // being sent:
  struct Queue_Element {
    E_Message_Type       message_type;
    const Machine_Char * message;
    
    Queue_Element(E_Message_Type t, const Machine_Char * m) {
      assert (m);
      message_type = t;
      message = (const Machine_Char *) strdup((const char *)m);
      if (message == 0)
        throw CardWords_XLowResources(gettext("CardWords_Participant::"\
                                            "Queue_Element: constructor: "\
                                            "strdup: Out of memory"));
    }
    ~Queue_Element() {
      if (message != 0) free ((void *)message);
    }
    Queue_Element( const Queue_Element & qe) {
      message_type = qe.message_type;
      message = (const Machine_Char *) strdup((const char *)qe.message);
      if (message == 0)
        throw CardWords_XLowResources(gettext("CardWords_Participant::"\
                                            "Queue_Element: "\
                                            "copy constructor: "\
                                            "strdup: Out of memory"));
    }
  };
  deque<Queue_Element> pending_messages_queue;

  bool sending_request; // True if we are currently sending a message.
  size_t bytes_to_send; // The number of bytes in the header/body that we
  //                       are sending.
  size_t bytes_sent; // The portion of bytes that have successfully been
  //                    sent already.
  Machine_Char *message; // A Pointer to the junk to send.

  // This function get called internally, after checking the queue or
  // with a message from the queue:
  void now_do_this_message (E_Message_Type, const Machine_Char *message);

  // receiving answers from dicbot:
  bool expecting_dicbot_answer;
  bool receiving_dicbot_answer;
  bool receiving_dicbot_header;
  
public:
  size_t get_received_requests (void) const {return received_requests;}
  size_t get_answered_requests (void) const {return answered_requests;}
  bool is_request_pending(void) const {
    assert (size_t(received_requests - answered_requests) <= 1);
    return received_requests != answered_requests;
  }
  bool is_dicbot_query_pending (void) const {return dicbot_query_pending;}
  size_t get_last_sent_info(void) const {return last_sent_info;}
  size_t get_last_personal_info(void) const {return last_personal_info;}

  // this method will immediately strdup(message), no need to keep it.
  void send_message (E_Message_Type, const Machine_Char *message);
  // The message send_message(personal_info, "BYE") will be recognized and
  // the connection will be closed after this message. The participant
  // will deregister from the selector and the engin and destroy itself.
  ~CardWords_Participant();

private:
  CardWords_Hand * hand; // The hand, if this participant is a player.

  int      points; // The collected points, if this participant is a player.

  CardWords_Game * engin; // A pointer to the game engin that handles moves,
  //                       trades and passes.

  bool my_go;
  
public:
  // fill the hand again:
  void refill_hand (void);
  
  CardWords_Hand * get_hand(void)const {return hand;}

  int get_points(void) const {return points;}
  int add_points(int score) {return points += score;}
  
  bool is_my_go(void) {return my_go;}

  void go (bool start_go = true);
  
  virtual void being_selected (int fd, int mode);
  virtual void timed_out (int fd, int mode);

  virtual void child_answers (const Machine_Char * answer);
  
  CardWords_Participant (int set_fd, // the socket filedescriptor
                       //             -1 for robots.
                       CardWords_Selector * set_selector,
                       CardWords_Game * set_engin);
  void robotize (void); // Become a robot, this is done when the connection
  //                       is broken or the client sends an invalid request.
};
#endif

