/* This file is part of cardwords
   (c) 1999 2000 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)
namespace CardWords {

class Selectible;

class Selector {
public:
  // The class 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 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 Selector as this is already done before the
  // call to 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;
  
  Selector ();
  ~Selector();

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

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

  void
  update_timeout (Selectible *,
                  int fd,
                  int mode,
                  time_t timeout_time = 0);
  
  // Call this and the being_selected() methods of the appropriate
  // 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 {
    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 Selectible {
private:
  Selectible(const Selectible &); // copy constructor must
  //                                                 not be used

public:
  Selectible(){};

  virtual
  ~Selectible() {};

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

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

