#include "Socket.h"

#include <sys/types.h>
#include <iostream>
#include <unistd.h>

#ifdef WIN32
#include <WinSock2.h>
#define socklen_t int
#else
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <netdb.h>
#endif

#ifdef WIN32
int Socket::m_nbSockets = 0;
#endif

Socket::Socket() throw (std::string) {

#ifdef WIN32
  if(m_nbSockets++ == 0) {
    WSADATA info;
    if (WSAStartup(MAKEWORD(2,0), &info)) {
      std::string err_msg("Could not start WSA");
      throw err_msg;
    }  
  }
#endif

  m_socket = 0;

  int s = socket(AF_INET, SOCK_STREAM, 0);
  if (s == -1) {
    std::string err_msg("Unable to create socket");
    throw err_msg;
  }

  m_socket = s;
}

Socket::Socket(const int s) {
  m_socket = s;
}

Socket::~Socket() {
  if (m_socket)
    close(m_socket);

#ifdef WIN32
  if (--m_nbSockets == 0) {
    WSACleanup();
  }
#endif
}

int Socket::Send(const char *data, const int length) throw (std::string) {
  int nb_sent = send(m_socket, data, length, 0);
  //std::clog << "Socket::Send: " << nb_sent << std::endl;

  if (nb_sent == -1) {
    std::string err_msg("Unable to send data");
    throw err_msg;
  }

  return nb_sent;
}
 
int Socket::Receive(const ssize_t buf_len, char * buf) throw (std::string) {

  ssize_t nb_received = recv(m_socket, buf, buf_len, 0);

  if (nb_received == -1) {
    std::string err_msg("Unable to receive data");
    throw err_msg;
  }

  return nb_received;
}

//////////////////////////////
SocketClient::SocketClient(const std::string &host, const int port) throw (std::string) 
  :Socket()
{
  //std::clog << "SocketClient::SocketClient: " << host << " - " << port << std::endl;

  std::string err_msg;
  sockaddr_in sa;
  int err;

  struct hostent *hs;
  hs = gethostbyname(host.c_str());

  if (!hs) {
    std::cerr << "SocketClient::SocketClient: gethostbyname failed" << std::endl;
    err_msg = strerror(errno);
    throw err_msg;
  }

  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  sa.sin_addr = *((in_addr *)hs->h_addr);
  memset(&(sa.sin_zero), 0, 8); 


  err = connect(m_socket, (sockaddr *) &sa, sizeof(sockaddr));

  if (err == -1) {
    std::cerr << "SocketClient::SocketClient: connect failed" << std::endl;
    err_msg = strerror(errno);
    throw err_msg;
  }
}

//////////////////////////////
SocketServer::SocketServer(const int port) throw (std::string) 
  :Socket()
{
  std::string err_msg;
  sockaddr_in sa;
  int err;

  memset(&sa, 0, sizeof(sockaddr_in)); 
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  
  err = bind(m_socket, (sockaddr *)&sa, sizeof(sockaddr_in));
  if (err == -1) {
    err_msg = strerror(errno);
    throw err_msg;
  }
  
  err = listen(m_socket, MAX_CLIENTS);
  if (err == -1) {
    std::string err_msg("Unable to receive data");
    throw err_msg;
  }
  
}

Socket* SocketServer::Accept() throw (std::string) {
  std::string err_msg;
  sockaddr_in sa;
  socklen_t sa_size = (socklen_t)sizeof(sockaddr_in);
  int client_socket;

  client_socket = accept(m_socket, (sockaddr *)&sa, &sa_size);

  if (client_socket == -1) {
    err_msg = "Accept failed";
    throw err_msg;
  }

  std::cerr << "Connection from " << sa.sin_addr.s_addr << std::endl;

  return new Socket(client_socket);
}
