#ifndef _UDP_SOCKET_H
#define _UDP_SOCKET_H

/*

UDP (UNIVERSAL DATAGRAM PROTOCOL) C++ CLASS...

Author: Matthew W. Coan
Date: Sat Jan  7 21:35:49 EST 2017

*/

#include <cstdio>
#include <cstdlib> 
#include <cstring>

#define OS_WINDOWS

#ifdef UNIX_OS
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif 

#ifdef OS_WINDOWS
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <assert.h>
#endif


#include <string>
#include <iostream>

#include "bsstream.h"

namespace net_tools {

using namespace std;
using namespace text_tools;

class udp_socket {
public:
   enum udp_type { UDP_SERVER, UDP_CLIENT };
   struct udp_exception { };

private:
   string host; 
   int port;
   struct  hostent  *ptrh;
   struct  protoent *ptrp;
   struct  sockaddr_in sad;
   SOCKET     fd;
   struct sockaddr fskt;
   int fskt_len;
   int n;

public: 
   udp_socket(const string & host, const int port, udp_socket::udp_type the_type) throw(udp_exception) {
      this->host = host;
      this->port = port;
      n = 0;

      fskt_len = sizeof(fskt);

      memset((char *)&sad,0,sizeof(sad));
      sad.sin_family = AF_INET;
      sad.sin_port = htons((u_short)port);

/*
      ptrh = gethostbyname(host.c_str());

      if ( ((char *)ptrh) == NULL ) {
         fprintf(stderr,"invalid host: %s\n", host.c_str());
         throw udp_exception();
      }
      memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);

      if ( ((int)(ptrp = getprotobyname("udp"))) == 0) {
         fprintf(stderr, "cannot map \"udp\" to protocol number");
         throw udp_exception();
      }
*/

      //fd = socket(AF_INET, SOCK_DGRAM, ptrp->p_proto);
      fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

      if (fd == INVALID_SOCKET) {
         fprintf(stderr, "socket creation failed\n");
         throw udp_exception();
      }

      switch(the_type) {
      case UDP_SERVER:
         sad.sin_addr.s_addr = htonl(INADDR_ANY);
         if (bind(fd, (struct sockaddr *)&sad, sizeof(sad)) == INVALID_SOCKET) {
            cerr << "unable to bind socket..." << endl;
            throw udp_exception();
         }
      break;

      case UDP_CLIENT:
         sad.sin_addr.s_addr = inet_addr(host.c_str());
         /*
         if (inet_aton(host.c_str(), &sad.sin_addr)==0) {
            cerr << "unable to inet_aton..." << endl;
            throw udp_exception();
         }
         */
      break;
      }
   }

   ~udp_socket() {
      closesocket(fd);
   }

   int send(const char * buf, const size_t size) {
      n = sendto(fd, buf, size, 0, (struct sockaddr *)&sad, sizeof(sad));
      return n;
   }
   
   int recive(char * buf, const size_t size) {
      n = recvfrom(fd, buf, size, 0, (struct sockaddr *)&sad, &fskt_len); 
      return n;
   }

   operator void*() {
      return ((void*)(n != -1));
   }
};

}

#endif /* _UDP_SOCKET_H */
