/* 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
*/

/* This file defines a class that inherits from CardWords_Selectible and
   CardWords_ChildProcess. Its purpose is to let the selector wait for
   communication possibilitywith the child.
*/

#ifndef CARDWORDS_SELECTIBLECHILD_HH
#define CARDWORDS_SELECTIBLECHILD_HH
#include "cardwords_childprocess.hh"
#include "cardwords_select.hh"
#include <deque>
#include "cardwords_exceptions.hh"
#include <string>

class CardWords_SelectibleChildLocker {
public:
  virtual void child_answers(const string & answer) = 0;
};

class CardWords_SelectibleChild : public CardWords_Selectible,
                                  public CardWords_ChildProcess
{
public:
  CardWords_SelectibleChild (CardWords_Selector * selector,
                             const string & progname);

  // ask_and_lock_child will try to lock this CardWords_SelectibleChild for
  // the access by a single CardWords_Selectible object. I will explain the
  // purpose for that locking:
  // First of all, this is *not* a multithreaded application, so we do not
  // need to bother with atomic locking mechanisms.
  // This CardWords_server receives however input from several different
  // network connections (sockets), and they all can request to ask dicbot a
  // question.
  // The requests are of course multiplexed via select(). But we have to give
  // a single requesting object the chance to ask dicbot a question and to
  // receive the *whole* answer from dicbot. This is not trivial as the byte
  // length of dicbot's answers are not predictable. So DicBot will send its
  // answers with a header stating how long this answer is, and the asking
  // object will have to read at least twice, one time the header, and the
  // second time the data of the answer as it knows now how long it is.
  // It is important that there is no other object trying to read the answer
  // to its request in the meantime, as it would only get the second part of
  // the answer to the first object.
  // lock_and_ask_child() takes a string to send and inserts the
  // CardWords_SelectibleChild object in the write queue of the selector.
  // After sending the string, it will insert itself in the read queue
  // and will forward the read events to the asking CardWords_Selectible
  // until this CardWords_Selectible object will release the lock on this
  // CardWords_Selectible_Child.
  // (The calling function does not know when the message will be send and
  // it does not need to know it.)
  void
  lock_and_ask_child (CardWords_SelectibleChildLocker * asking_object,
                      const string & question);

  void
  stop_communication(void); // Disallows further locking.
private:
  // release the lock:
  void release_lock (CardWords_SelectibleChildLocker * asking_object);

  // Queueing requests that can not be executed immediately because another
  // Object already has a lock here:
  struct Queue_Element {
    
    CardWords_SelectibleChildLocker * selectible;
    
    const string question;
    
    Queue_Element(CardWords_SelectibleChildLocker * s,
                  const string & q)
      : selectible(s), question(q)
      {assert (s);};
    
    Queue_Element( const Queue_Element & qe)
      : selectible(qe.selectible), question(qe.question) {};
  };
  
  deque<Queue_Element> awaiting_lock_queue;
private:
  // this function is called either directly by lock_and_ask_child if no other
  // lock is present or by release_lock that gets the next lock from the
  // queue:
  void lock (CardWords_SelectibleChildLocker * asking_object,
             const string & question);
  
public:
  // providing a method to be called by the selector:
  virtual void being_selected (int fd, int mode);

  virtual void timed_out (int fd, int mode);
  
private:
  // info about the current status  


  bool locked;
  bool sending_question; // Only valid if locked == true.
  bool receiving_answer; // Only valid if locked == true.
  bool communication_stopped;

  size_t bytes_sent; // Only valid if locked == true and
  //                    sending_question == true. Used to determine how many
  //                    bytes of current_question have already been sent and 
  //                    how many are still waiting for transmission.
  string current_question;

  size_t bytes_expected, bytes_received;
  bool receiving_header, receiving_body;
  string current_answer;
  

  CardWords_SelectibleChildLocker *locker; // The locking object.
  //                             Only valid if locked == true

  // The global selector:
  CardWords_Selector * selector;
};

#endif
