/* 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:
  // Every participant must have a name. If name is "", then the
  // participant is still in logging in phase:
  string name;
public:
  bool has_name (void) const {return name.length() != 0;}
  const string & get_name (void) const {return name;}
  void set_name (const string & newname);

private:
  // Password is required for owner and perhaps also for players for
  // continuing a saved game.
  bool password_required;
  bool password_approved;

public:
  bool need_password (void) const { return password_required;}

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 currently.
  string request; // 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;
    string         message;
    
    Queue_Element(E_Message_Type t, const string & m) {
      assert (m.length() != 0);
      message_type = t;
      message = m;
    }
    ~Queue_Element() {}

    Queue_Element(const Queue_Element & qe) {
      message_type = qe.message_type;
      message = qe.message;
    }
  };
  deque<Queue_Element> pending_messages_queue;

  bool sending_request; // True if we are currently sending a message.
  string message; // The junk to send.

  bool sending_MESSAGE_BYE; // MESSAGE_BYE is the last message.
  
  // 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  string & 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  string & message);
  // FIXME: Do again something like this:
  // 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 string & 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
