#ifndef __FLX_DEMUX_POSIX_DEMUXER_H__
#define __FLX_DEMUX_POSIX_DEMUXER_H__

// base classes for posix style demuxers

#include "demux_demuxer.hpp"

namespace flx { namespace demux {
class DEMUX_EXTERN posix_demuxer;            // fwd decl

// abc
class DEMUX_EXTERN posix_wakeup {
public:
  virtual ~posix_wakeup() {}

  // when called, the wakeup has finished and been removed.
  virtual void wakeup(posix_demuxer& demux) = 0;
};

class DEMUX_EXTERN socket_wakeup : public posix_wakeup {
public:
  int   s;                // the non blocking socket
  int   wakeup_flags;         // set on wakeup, r/w or both

  socket_wakeup() : s(-1) {}
};

class DEMUX_EXTERN posix_demuxer : public demuxer {
protected:
  void async_quit(); // useful for requesting wait thread quit in
                     // thread safe demuxer destructors. doesn't throw.

public:
  virtual ~posix_demuxer();

  // posix style sockets. for reading and writing (but not both at once
  // for the same socket_wakeup) you are guaranteed to receive only one
  // wakeup per call to this function when you call wait.
  // returns -1 if no wakeup is coming and zero if one is.
  // For simultaneous reading and writing you may get two wakeups,
  // that is, it may violate the "one shot" rule. Ignoring for now,
  // as it's not a common use. This makes it undefined behaviour.
  // wakeup is owned by the demuxer until its wakeup is called,
  // so treat it with kid gloves, i.e. don't mess with it.
  virtual int   add_socket_wakeup(socket_wakeup* sv, int flags) = 0;

  // to be called when we can read & write without blocking
  // return true if connection closed, update pb
  // sort of a strange place to have this..., more a socket wakeup
  // thing, even if static
  static bool   socket_recv(int s, sel_param* pb);
  static bool   socket_send(int s, sel_param* pb);
};

// some handy control blocks for common non-blocking socket operations
// note that they "fortuitously" both have start methods. hmm.
// a socket io one could be handy here.

// this one's restartable (makes sense for listener sockets)
class DEMUX_EXTERN accept_control_block : public socket_wakeup {
public:
  int   accepted;   // accepted socket (out)
  int   socket_err;   // the error, if acceptee == -1, else 0 (out)

  accept_control_block() : accepted(-1), socket_err(0) {}

  virtual int start(posix_demuxer& demux);
  virtual void wakeup(posix_demuxer& demux);
};

class DEMUX_EXTERN connect_control_block : public socket_wakeup {
public:
  int     socket_err;   // outgoing error (on start or wake)
  // this should probably be a sockaddr type
  const char* addy;     // addr (dotted quad) (in)
  int     p;        // port (in)

  connect_control_block() : socket_err(0) {}

  virtual int start(posix_demuxer& demux);
  virtual void wakeup(posix_demuxer& demux);

  // oops, can't check for s != -1 as it's always there.
  // was always "finished" and so I started io, losing the first wakeup
  // on epoll evtsrc. Is this right, or should it be != EINPROGRESS?
  // keep in sync with iocp version. give socket_err initial definition
  // that works with this?
  bool finished() { return ( 0 == socket_err); }
};

}} // namespace demux, flx
#endif