// ======================================================================
//
// Simple socket class code
//
// Stream sockets only, but datagrams could be added.
//
// Lots could be done here.  For example, all of the reading and writing
// for a socket could be handled in the class, as well as informational
// services, timers, broadcasts and basically everything else.  The
// following is simply for the purposes of demonstration.
//
// ======================================================================


#include "socket_class.h"

// ======================================================================

// ServerSocket Methods 

// =====================================================
//
// Open a server socket for a stream connection.
//
// ======================================================

ServerSocket::ServerSocket (int port)
{
   int           lsock;
   sockaddr_in   addr;

   error = 0;

   // Open a socket

   lsock = socket (AF_INET, SOCK_STREAM, 0);
   if (lsock == -1) 
   {
      error = SOCKET_OPEN_FAIL;
      return; 
   } 

   // Bind the socket to the local port and set it up to
   // listen.

   bzero ((char *) &addr, sizeof (addr));
   addr . sin_family = AF_INET;
   addr . sin_port = htons ((short) port);
   addr . sin_addr . s_addr = htonl (INADDR_ANY);

   if (bind (lsock, (const sockaddr *) &addr, sizeof (addr)) < 0)
   {
      error = SOCKET_BIND_FAIL;
      sock = -1;
      close (lsock);
      return;
   }

   // Do the listen.  This is actually not a good place for this, since the
   // listen should be handled by the application which is in a better
   // position to know when it is time to listen.

   if ((listen (lsock, 5)) < 0)
   {
      error = SOCKET_LISTEN_FAIL;
      sock = -1;
      close (lsock);
      return;
   }

   // Save the socket number in the class private variable.

   sock = lsock;
}

// ============================================================
//
// Accept and connection and create a new socket for the client.
//
// =============================================================

Socket ServerSocket::Accept ()
{
   int        newsock;
   sockaddr   addr;
   int        addr_len;

   error = 0;

   // Do the accept, and when it returns, return the new Socket
   // for use by the server.

   newsock = accept (sock, &addr, &addr_len);

   Socket s = Socket (newsock);

   return (s);
}

// =====================================================================
//
// Return the socket number for use by the program.
//
// =====================================================================

int ServerSocket::GetSocketNum ()
{
   error = 0;

   return (sock);
}

void ServerSocket::Close ()
{
   error = 0;

   shutdown (sock, 2);
   close (sock);
}

// =====================================================================
//
// Return the current error code for the socket.
//
// =====================================================================

int ServerSocket::Error ()
{
   return (error);
}

// =====================================================================
//
// Return an error message for the current error.  Not finished.
//
// =====================================================================

char *ServerSocket::ErrorMessage ()
{
   return ("Some kind of error");
}


// Socket Methods ========================================================

// ===============================================================
//
// Open a client or peer socket for a stream connection.
//
// ================================================================


Socket::Socket (char *host, int port)
{
   sockaddr_in     addr;
   int             lsock;
   unsigned int    iaddr;
   hostent         *hostent;

   error = 0;
   
   // Open a socket

   lsock = socket (AF_INET, SOCK_STREAM, 0);
   if (lsock == -1) 
   {
      error = SOCKET_OPEN_FAIL;   
      return; 
   } 

   // Set up an address and connect.

   hostent = gethostbyname (host);
   if (hostent == NULL)
   {
      error = DNS_DB_ERROR;
      close (lsock);
      sock = -1;
      return;
   }

   addr.sin_family = AF_INET; 
   addr.sin_port = htons (port); 
   memcpy (&iaddr, hostent->h_addr_list[0], 4);
   addr . sin_addr . s_addr = iaddr;
   
   if (connect(lsock, (sockaddr *) &addr, sizeof (struct sockaddr_in)) == -1) 
   {    
      error = SOCKET_CONNECT_FAIL;
      close (lsock);
      Socket::sock = -1;
      return; 
   }  

   Socket::sock = lsock;;
}

// ====================================================================
//
// Create a socket for an already open file descriptor (as from accept)
//
// =====================================================================


Socket::Socket (int lsock)
{
   sockaddr_in     addr;
   
   // For a socket opened by the system where you just need the class
   // to support the methods.

   sock = lsock;
}

// =====================================================================
//
// Return the socket number for use by the program.
//
// =====================================================================

int Socket::GetSocketNum ()
{
   error = 0;
   
   return (sock);
}


// =====================================================================
//
// Shutdown and close the socket.
//
// =====================================================================

void Socket::Close ()
{
   error = 0;
   
   shutdown (Socket::sock, 2);
   close (Socket::sock);
}

// =====================================================================
//
// Return the current error code for the socket.
//
// =====================================================================

int Socket::Error ()
{
   return (error);
}

// =====================================================================
//
// Return an error message for the current error.  Not finished.
//
// =====================================================================

char *Socket::ErrorMessage ()
{
   return ("Some kind of error");
}