// NetInit
// NetRead
// NetWrite
// CloseSocket
// GetHostInfo

#ifndef NETWORK_H
#define NETWORK_H


/*************\
*             *
*   Windows   *
*             *
\*************/

#ifdef WIN32

#include <winsock.h>

// Must link with ws2_32.lib
#pragma comment(lib, "ws2_32.lib")


// Initialisation
inline int NetInit(int a = 2, int b = 2)
{
  WSADATA wsaData;

  return (WSAStartup((a) | (b << 8), &wsaData));
}

// Close
inline int CloseSocket(int sock) { return closesocket(sock); }




/*************\
*             *
*    Linux    *
*             *
\*************/

#elif defined LINUX

#include <unistd.h>		// UNIX stuff
#include <netdb.h>		// Access to DNS lookups
#include <sys/socket.h>	// Socket functions
#include <arpa/inet.h>	// inet_ntoa function

// Initialisation
inline int NetInit(int a = 2, int b = 2) { return 0; }


// Close
inline int CloseSocket(int sock) { return close(sock); }




/*************\
*             *
*    Bad OS   *
*             *
\*************/


#else

#error ERROR: OS not supported

#endif


// NetRead
inline int NetRead(int sock, char * buffer, int size, int flags = 0)
{
  return (recv(sock, buffer, size, flags));
}

// NetWrite
inline int NetWrite(int sock, char * buffer, int size, int flags = 0)
{
  return (send(sock, buffer, size, flags));
}





#define MAX_HOST_NAME	1024

// Get the local host name and IP
inline std::vector<std::string> GetHostInfo()
{
  std::vector<std::string> Return;

  char Buffer[MAX_HOST_NAME];
  gethostname(Buffer, MAX_HOST_NAME);
  Return.push_back(std::string(Buffer));

  hostent *local = gethostbyname(Buffer);
  Return.push_back(std::string(inet_ntoa(*(in_addr *)*local->h_addr_list)));

  return Return;
}


class CNetworkException
{
  std::string ErrorString;

 public:
  CNetworkException(const std::string Mess) : ErrorString(Mess) {}
  std::string GetErrorString() const { return ErrorString; }
};



#endif // NETWORK_H




// hostent * gethostbyname(const char *name)
// returns NULL on error

// htons(int port)
// Converts values between the host and network byte order.
// Specifically, htons() converts 16-bit quantities from host
// byte order to network byte order.


// int socket(int af, int type, int protocol)
// af = PF_INET, type = SOCK_STREAM, protocol = IPPROTO_IP
//
// // SOCK_STREAM = TCP, SOCK_DGRAM = UDP
// sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
// if (sock < 0)
//   {
//     printf("Unable to create a socket: %s\n", strerror(errno));
//   }


// Creating sockaddr_in structure
//
// struct sockaddr_in sa; /* Connection address. */
// memset(&sa, 0, sizeof(struct sockaddr_in));
// sa.sin_port = htons(port);
// memcpy(&sa.sin_addr, hostlist->h_addr_list[0], hostlist->h_length);
// sa.sin_family = AF_INET;


// Sending (UDP)
//
// if (sendto( sock,                   /* initialized UDP socket */
//             message,                /* data to send */
//             strlen(message)+1,      /* msg length + trailing NULL */
//             0,                      /* no special flags */
//             (struct sockaddr *)&sa, /* destination */
//             sizeof(struct sockaddr_in)
//           ) <= (int)strlen(message))
//   {
//     printf("Error sending packet: %s\n", strerror(errno));
//   }

// Receiving (UDP)
//
// if (recvfrom ( sock,/* UDP socket */
//                buf,/* receive buffer */
//                255,/* max bytes to receive */
//                0,/* no special flags */
//                (struct sockaddr *) &sa,/* sender's address */
//                &sa_len
//              ) < 0)
//   {
//     printf("Error receiving packet: %s\n", strerror(errno));
//   }



// Connecting (TCP) (client)
//
// if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
//   {
//     printf("Unable to connect: %s\n", strerror(errno));
//   }

// Listening for a connection (TCP) (server)
//
// listener = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
// if (bind(listener, (struct sockaddr *)&sa, sa_len) < 0)
//   {
//     printf("Unable to bind to port %i: %s\n", port, strerror(errno));
//     return 1;
//   }
//
// if (listen(listener, 1) < 0) // Only one client can connect
//   {
//     printf("Unable to listen: %s\n", strerror(errno));
//     return 1;
//   }
//
// int client;		// socket for the client
//
// client = accept(listener, (struct sockaddr *)&sa, &sa_len);
// if (client < 0)
//   {
//     printf("Unable to accept: %s\n", strerror(errno));
//     return 1;
//   }

// NetRead
// Waits until it can return at least one byte.  Returns the number
// of bytes read.  < 0 on error.  0 if the connection has been closed.
//
// amt = NetRead(sock, &ch, 1);
// if (amt < 0)
//   {
//     printf("\nNetRead error: %s\n", strerror(errno));
//   }
// else if (amt == 0)
//   {
//     printf("\nConnection closed by remote system.\n");
//   }

// NetWrite
// Sends on the server.  Returns the number of bytes writen. < 0 On error.
// 0 if the connection has been closed.
//
// /* Figure out how much data we need to send. */
// length = strlen(sendbuf);
// sent = 0;
//
// /* Repeatedly call write until the entire buffer is sent. */
// while (sent < length)
//   {
//     int amt;
//     amt = NetWrite(client, sendbuf+sent, length-sent);
//     if (amt <= 0)
//       {
//         // Zero-byte writes are OK if they are caused by signals (EINTR).
//         // Otherwise they mean the socket has been closed.
//         if (errno == EINTR)
//           continue;
//         else
//           {
//             printf("Send error: %s\n", strerror(errno));
//             break;
//           }
//       }
//
//     // Update our position by the number of bytes that were sent.
//     sent += amt;
//   }
