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

// defining the classes CardWords_Selectible and CardWords_Selector here

#ifndef CARDWORDS_SELECT_HH
#define CARDWORDS_SELECT_HH

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <set>
#include <stdio.h>
#include <errno.h>

#define READ_MODE (CardWords_Selector::read)
#define WRITE_MODE (CardWords_Selector::write)
#define EXCEPTION_MODE (CardWords_Selector::exception)

class CardWords_Selectible;

class CardWords_Selector {
public:
  // The class CardWords_Selector sleeps on the given file descriptors using
  // select. File descriptors can be registered with in the usual modes
  // read, write, exception. A timeout in seconds since epoch can optionally
  // be given. If the desired state of the filedescriptor is reached, the
  // being_selected() method of the registered CardWords_Selectible will be
  // called.
  // If the timeout occurrs before that, the timed_out() method will be
  // called. This function does not need to remove the timed out file
  // descriptor from the CardWords_Selector as this is already done before the
  // call to CardWords_Selectible::timed_out().

  // FIXME: implement timeouts!
  
  enum select_mode {read, write, exception};

  enum select_priority {most_important, more_important, important,
                        less_important, unimportant};
  static const select_priority accept_priority = more_important;
  static const select_priority owner_priority = most_important;
  static const select_priority player_priority = less_important;
  static const select_priority watcher_priority = unimportant;
  static const select_priority dicbot_priority = important;
  
  CardWords_Selector ();
  ~CardWords_Selector();

  void add (CardWords_Selectible *,
            int fd,
            int mode,
            time_t timeout_time = 0);

  void remove (CardWords_Selectible *, int fd, int mode);

  void update_timeout (CardWords_Selectible *,
                       int fd,
                       int mode,
                       time_t timeout_time = 0);
  
  // Call this and the being_selected() methods of the appropriate
  // CardWords_Selectible objects will be called;
  int run (bool looping = true);

  // make run return:
  void quit(int);
private:
  bool run_looping;
  int  run_return_value;
  
  struct Member {
    CardWords_Selectible * selectible;
    int fd;
    int mode;
    time_t timeout_time;
    bool operator < (const Member &m) const {
      if (selectible != m.selectible) return selectible < m.selectible;
      if (fd != m.fd) return fd < m.fd;
      return mode < m.mode;
      // that means consider two members with different timeouts as equal!
    }
  };
  set<Member> set;
  ::set<Member>::iterator next_selected;
};


class CardWords_Selectible {
public:
  CardWords_Selectible(){};
  CardWords_Selectible(const CardWords_Selectible &); // copy constructor must
  //                                                 not be used
  virtual ~CardWords_Selectible() {};

  virtual void being_selected (int fd, int mode)= 0;
  virtual void timed_out (int fd, int mode)= 0;
};

#endif
