#ifndef ESOCKET_H
#define ESOCKET_H

#include "eutils.h"

#ifdef _WIN32
 #include <winsock2.h>
 #include <windows.h>
#else
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <netinet/in.h>
 #include <netdb.h>
#endif

#include "estr.h"
#include "earray.h"

#include "efunc.h"
#include "esystem.h"

#ifdef _WIN32
 #define socklen_t int
 #define ESETOPT char
 #define MSG_NOSIGNAL 0
#else
 #define ESETOPT int
#endif

class eudpsocket
{
 public:
  int                fhsock;
  struct sockaddr_in faddr_in;

  estr               faddress;
  int                fport;
  esystemCallback   *sCallback;
#ifdef _WIN32
  WSAEVENT wsaEvent;
#endif

  efunc onSend;
  efunc onReceive;
  efunc onClose;

  eudpsocket();
  eudpsocket(const eudpsocket& s);
  eudpsocket(int hsock,struct sockaddr_in addr_in);
  virtual ~eudpsocket();

  void setCallback();
  void unsetCallback();

//  bool connect(const estr& address,int port);
  bool bind();
  bool listen(int port,const estr& address="0.0.0.0",bool reuseAddress=false);
  void close();
  void shutdown();

  void setBroadcast(bool value);
  void setReuseAddress(bool value);
  void setBlocking(bool value);

  // user defined functions
  virtual void doClose();
  virtual void doRecv();
  virtual void doSend();

  // internal functions
  void _doRecv();
  void _doClose();
  void _doSend();

  int sendto(const estr& data,const estr& address,int port);
  int recvfrom(estr& data,estr& address,int& port);
};

class esocket
{
 public:
  int                fhsock;
  struct sockaddr_in faddr_in;
  estr               faddress;
  int                fport;
  esystemCallback   *sCallback;
#ifdef _WIN32
  WSAEVENT wsaEvent;
#endif

  efunc onSend;
  efunc onReceive;
  efunc onClose;

  esocket();
  esocket(const esocket& s);
  esocket(int hsock,struct sockaddr_in addr);
  virtual ~esocket();

  void init();
  void setCallback();
  void unsetCallback();
  void enableReadCallback();
  void enableWriteCallback();
  void disableReadCallback();
  void disableWriteCallback();

  bool connect(const estr& address,int port);
  void close();
  void shutdown();
  void wait();

  efunc onIncoming;
  virtual void doIncoming();

  bool listen(int port,const estr& address="0.0.0.0");
  bool accept(esocket &socket);

  void setBlocking(bool value);
  void setTCPNoDelay(bool value);
  void setTCPQuickAck(bool value);

  // user subclassed functions
  virtual void doConnect();
  virtual void doClose();
  virtual void doRecv();
  virtual void doSend();

  // internal functions
  virtual void _doIncoming();

  void _doSend();
  void _doRecv();
  void _doClose();

  int recv(estr& buffer,int maxlen=(1u<<16u));

  inline int recv(char *buffer, int len,int flags=0){
    return(::recv(fhsock,buffer,len,flags));
  }

  inline int send(const estr& buffer){
    #if defined(__APPLE__) || defined(_WIN32)
    return(send(buffer._str,buffer.len()));
    #else
    return(send(buffer._str,buffer.len(),MSG_NOSIGNAL));
    #endif
  }

  inline int send(char *buffer, int len, int flags=0){
    return(::send(fhsock,buffer,len,flags));
  }
};

#if !defined(_WIN32)
class enamedsocket : public esocket
{
 public:
  struct sockaddr_un faddr_un;

  enamedsocket();
  enamedsocket(const enamedsocket& s);

  void init();

  void _doIncoming();

  bool connect(const estr& file);
  bool listen(const estr& file);
  bool accept(enamedsocket &socket);
};
#endif



/*
template <class K>
class eserverClient : public esocket
{
 public:
  T& server;
  eserverClient(T& server);
};

template <class K>
eserverClient<K>::eserverClient(T& _server): server(_server) {}
*/

/*
class eserver : public esocket
{
 public:
  earray<esocket> sockets;

  efunc onAccept;

  virtual void doAccept(esocket& socket);
  void doIncoming();
};
*/

#endif
