#ifndef Socket_HH
#define Socket_HH
//#define Socket_DEBUG

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>

#include <vector>

#include "SockAddrIn.hh"


const int MAXHOSTNAME = 200;
const int MAXCONNECTIONS = 5;
const int MAXRECV = 500;


class Socket
{
 public:
  Socket() : m_sock(-1) {
    //    memset ( &m_addr, 0, sizeof ( m_addr ) );
    m_addr.sin_family = AF_INET;
  }

  virtual ~Socket()  {
    if ( is_valid() ) ::close ( m_sock );
  }

  // Listener initialization
  bool create() {
    m_sock = socket ( m_addr.sin_family,
		      SOCK_STREAM,
		      0 );

    if ( ! is_valid() )
      return false;


    // TIME_WAIT - argh
    int on = 1;
    if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
      return false;
    

    return true;
  }

  bool bind ( const int port ) {
    if ( ! is_valid() )
      {
	return false;
      }


    //  m_addr.sin_family = AF_INET;
    m_addr.sin_addr.s_addr = INADDR_ANY;
    m_addr.sin_port = htons ( port );

    int bind_return = ::bind ( m_sock,
			       ( struct sockaddr * ) &m_addr,
			       sizeof ( m_addr ) );
    
    if ( bind_return == -1 )
      {
	return false;
      }
    
    return true;
  }

  bool listen() const {
    if ( ! is_valid() )
      {
	return false;
      }
    
    int listen_return = ::listen ( m_sock, MAXCONNECTIONS );
    

    if ( listen_return == -1 )
      {
	return false;
      }
    
    return true;
  }

  // Server initialization
  bool accept ( const Socket& listener) {
    int addr_length = sizeof ( m_addr );
    m_sock = ::accept ( listener.m_sock, 
			( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );

    std::cout << "addr_length = " << addr_length << std::endl;
    std::cout << "sizeof ( m_addr ) = " << sizeof ( m_addr ) << std::endl;

    assert(addr_length==sizeof ( m_addr ));
    
    if ( m_sock <= 0 )
      return false;
    else
      return true;
  }

  // Client initialization
  bool connect ( const std::string host, const int port ) {
  if ( ! is_valid() ) return false;

  //  m_addr.sin_family = AF_INET;
  m_addr.sin_port = htons ( port );

  int status = inet_pton ( m_addr.sin_family, host.c_str(), &m_addr.sin_addr );

  std::cout << "inet_pton status = " << status << std::endl;


  if ( errno == EAFNOSUPPORT ) return false;

  status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) );

  if ( status == 0 )
    return true;
  else
    return false;
  }

  // Make don't wait
  bool noDelay() {
    std::cerr << "Setting TCP_NODELAY" << std::endl;

    int flag = 1;
    int result = setsockopt(m_sock,            /* socket affected */
			    IPPROTO_TCP,     /* set option at TCP level */
			    TCP_NODELAY,     /* name of option */
			    (char *) &flag,  /* the cast is historical
						cruft */
			    sizeof(int));    /* length of option value */
    
    return result==0;
  }


  // Data Transimission
  bool send ( const std::string ) const;
  int recv ( std::string& ) const;

  //  bool send ( int ) const;
  //  int recv ( int& ) const;

  //template<class T> bool Socket::send(const T* t, unsigned len) {
  template<class T> bool send(const T* t, unsigned len) {
    
    char *c((char*)t);

    int bytes(0);
    //int maxBytes(8*1024);
    int maxBytes(64*1024);

    _sendBytes=len*sizeof(T);
    _sendStatus.clear();

    while(bytes<int(len*sizeof(T))) {

      int sendBytes(len*sizeof(T)-bytes);
      if(sendBytes>maxBytes) sendBytes=maxBytes;

      //std::cout << "send from 0x" << hex << (unsigned)(c+bytes) << dec
      //	<< ", length " << sendBytes << std::endl;

      int status = ::send ( m_sock, c+bytes, sendBytes, MSG_NOSIGNAL );
      _sendStatus.push_back(status);

      if ( status < 0 ) {
	std::cerr << "send status = " << status << " expected " 
		  << sendBytes << std::endl;
	std::cerr << "status = " << status << " errno == " << errno 
		  << "  in Socket::send\n";
	perror(0);
	return false;
      }

      if ( status == 0 ) {
	std::cerr << "send status = " << status << " expected " 
		  << sendBytes << std::endl;
	perror(0);
	return false;
      }

      if ( status != sendBytes ) {
	std::cerr << "send status = " << status << " expected " << sendBytes
		  << " of " << len << "*" << sizeof(T) << std::endl;
	return false;
      } 

      //int reply(0);
      //      ::recv ( m_sock, &reply, 4, 0 );
      bytes+=sendBytes;
    }
    return true;
  }

  //template<class T> int Socket::recv(T* t, unsigned len) {
  template<class T> int recv(T* t, unsigned len) {

    memset ( t, 0, len*sizeof(T) );
    char *c((char*)t);

    _recvBytes=len*sizeof(T);
    _recvStatus.clear();

    int bytes = 0;
    while(bytes<int(len*sizeof(T))) {
      //std::cout << "recv into 0x" << hex << (unsigned)(c+bytes) << dec
      //	<< ", length " << len*sizeof(T)-bytes << std::endl;

      int status = ::recv ( m_sock, c+bytes, len*sizeof(T)-bytes, 0 );
      _recvStatus.push_back(status);

      if ( status < 0 ) {
	std::cerr << "recv status = " << status << " expected " 
		  << len*sizeof(T) << std::endl;
	std::cerr << "status = " << status << " errno == " << errno 
		  << "  in Socket::recv\n";
	perror(0);
	return 0;
      }

      /*
      if ( status == 0 ) {
	std::cerr << "recv status = " << status << " expected " 
		  << len*sizeof(T) << std::endl;
	perror(0);
	return 0;
      }
      */

      if ( status != int(len*sizeof(T))-bytes ) {
	//std::cout << "recv status = " << status << " expected " 
	//<< len*sizeof(T)-bytes << std::endl;

		/*
		for(int i(bytes);i<bytes+status;i++) {
		  std::cout << std::setw(3) << i << " " << printHex((unsigned char)c[i]) << std::endl;
		}
		*/

      }

      //      int reply(0);
      //      ::send ( m_sock, &reply, 4, MSG_NOSIGNAL );

      bytes+=status;
    }

    int len2=bytes/sizeof(T);
    return len2;
  }

  //template<class T> bool Socket::send(const T& t) const {
  template<class T> bool send(const T& t) const {
    int status = ::send ( m_sock, &t, sizeof(T), MSG_NOSIGNAL );
    if ( status != sizeof(T) )
      {
	return false;
      } 
    else
      {
	return true;
      }  
  }

  //template<class T> int Socket::recv(T& t) const {
  template<class T> int recv(T& t) const {
    char buf [ MAXRECV + 1 ];

    t = 0;

    memset ( buf, 0, MAXRECV + 1 );

    T* p(&t);
    int status = ::recv ( m_sock, p, sizeof(T), 0 );

    std::cerr << "recv status = " << status << std::endl;

    if ( status == -1 )
      {
	std::cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
	return 0;
      }
    else if ( status == 0 )
      {
	return 0;
      }
    else
      {
	//      s = buf;
	return status;
      }
  }

  void set_non_blocking ( const bool b) {
  int opts;

  opts = fcntl ( m_sock,
                 F_GETFL );

  if ( opts < 0 )
    {
      return;
    }

  if ( b )
    opts = ( opts | O_NONBLOCK );
  else
    opts = ( opts & ~O_NONBLOCK );

  fcntl ( m_sock,
          F_SETFL,opts );

  }

  bool close() {
    if ( ! is_valid() )
      {
	return false;
      }
    
    int close_return = ::close ( m_sock );
    
    if ( close_return == -1 )
      {
	return false;
      }
    
    return true;
  }

  bool is_valid() const { 
    return m_sock != -1;
  }

  bool usable() const { 
    return m_sock != -1;
  }

  int fileDescriptor() const {
    return m_sock;
  }


  void print(std::ostream &o) const {
    o << "Socket at " << m_sock << std::endl;
    m_addr.print(o);
  }

  void printSendInfo(std::ostream &o) {
    o << "Socket::printSendInfo()  _sendBytes = " << _sendBytes
      << " _sendStatus length = " << _sendStatus.size() << std::endl;
    for(unsigned i(0);i<_sendStatus.size();i++) {
      o << " _sendStatus element " << i << " = "
	<< _sendStatus[i] << std::endl;
    }
  }

  void printRecvInfo(std::ostream &o) {
    o << "Socket::printSendInfo()  _recvBytes = " << _recvBytes
      << " _recvStatus length = " << _recvStatus.size() << std::endl;
    for(unsigned i(0);i<_recvStatus.size();i++) {
      o << " _recvStatus element " << i << " = "
	<< _recvStatus[i] << std::endl;
    }
  }


private:
  int m_sock;
  SockAddrIn m_addr;

  unsigned    _sendBytes;
  std::vector<int> _sendStatus;
  unsigned    _recvBytes;
  std::vector<int> _recvStatus;
};

#endif
