#ifndef LIBEMB_SOCKET_H /************************************************************************* -*- c++ -*- **/
#define LIBEMB_SOCKET_H

/****************************************************************************************************************/

#include "descriptor.h"
#include "timer.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/****************************************************************************************************************/

class Socket : public Descriptor {

public: // Construction
  Socket(Object *p = 0, const char *n = 0);
  virtual ~Socket();

public:
  void setVerbose(int n)  { m_verbose = n;    }
  int verbose(void) const { return m_verbose; }

public: // Set address access

  // PF_INET
  int setAddress(const struct in_addr &ipaddr, int port);

  int setIPAddress(const struct in_addr &ipaddr);
  int setPort(int port);

  int setAddress(const char *txt, int port);
  int setHost(const char *txt);

  // PF_UNIX
  int setPath(const char *path);

  // PF_UNIX, linux namespace
  int setSocketName(const char *n);

public: // Query address information
  // AF_INET
  int port(void) const;
  const struct in_addr *ip(void) const;
  const char *host(void) const;
  static int port(const struct sockaddr *sa);
  static const struct in_addr *ip(const struct sockaddr *sa);
  static const char *host(const struct sockaddr *sa);

  // AF_UNIX
  const char *path(void) const;
  static const char *path(const struct sockaddr *sa);

  // AF_UNIX, linux namespace
  const char *socketName(void) const;
  static const char *socketName(const struct sockaddr *sa);

  // Any
  const char *address(void);
  const char *address(const struct sockaddr *sa); // Placed into a buffer within this object


public: // Buffering
  bool setRxBuffer(size_t size);

public: // Keepalive support
  bool setKeepAlive(int ms = 2000);

public: // Connection handling
  int connect(bool blocking = false);
  int reconnect(void);
  void close(void);
  int listen(int backlog = 4);

public: // Connection information
  bool isConnected(void)       { return m_state == stConnected;  }
  bool connectionPending(void) { return m_state == stConnecting; }

public: // Callback from Descriptor class
  virtual void readyRead(void);					// REIMP
  virtual void readyWrite(void);				// REIMP

protected: // Callback for derived class
  virtual void onRead(const void *data, size_t len);		// Called when data has been received (STREAM)
  virtual void onWrite(void);					// Called when socket is ready for writing
  virtual void onClosed(void);					// Called when connection was closed
  virtual void onConnected(void);				// Called when connection is established
  virtual void onConnectFailed(void);				// Called when non-blocking connection failed to establish
  virtual Socket *onIncoming(const struct sockaddr *peer);	// Called on incoming connection

private: // Private data
  int doSetPath(const char *p, int offset);
  int obtainSocket(void);
  int setFamily(int f);
  void rxListen(void);
  void rxRead(void);
  int tryLater(void);
  void cleanup(void);

protected:
  virtual void timeout(Timer *t);

private:
  enum State {
    stDisconnected,
    stListening,
    stConnecting,
    stConnected,
  };

  State m_state;

  bool m_server;

  struct sockaddr *m_address;
  socklen_t m_addrlen;

  int m_verbose;

  char *m_address_text;

  unsigned char *m_rxbuf;
  size_t m_rxbufsize;

  Timer *m_keepalive_timer;
  int m_keepalive_time;
};

/****************************************************************************************************************/

#endif
