/*  shared library frontend for qwixx
 *  create the so or dll with:
		gcc -shared -o libfester.dll -fPIC fester.c .\http-server.o .\server.o -lws2_32 
	compile the example
		gcc .\example.c -L . -lfester -lws2_32
 */


#include "server.h"
#include "fester.h"
#include "http-server/http-server.h"

/*  Handles the setup of a socket for the use by the server.  The steps to
 *  get and configure a socket on an Operating System are:
 *    1. request a socket file descriptor from the OS
 *    2. optionally set the reuse flag for this socket
 *    3. set the non-blocking flag for this socket
 *    4. request that the socket has a reserved port exclusive for our use
 *    5. set socket passive, incoming connections are queued by the OS
 *
 *  The system calls for steps 1-5 are: socket, setsockopt, fcntl, bind, listen
 *  
 *  The setup process will step through each of these system calls and return
 *  the file descriptor.  Any error will result in the system error printed
 *  to standard error and a zero returned to the caller, the program should
 *  exit.  To improve on this, just use the same system calls in a new function
 *  that can handle the various errors.
 *
 *  setup makes use of the getaddrinfo helper function which when using Msft
 *  products only exists in their Winsock2 API.  There is not that big of a
 *  difference not using getaddrinfo, but it does make it easier for the user
 *  to switch to either a IPV4 or IPV6 address scheme easier.
 *
 *  [?] The pre-processor macro, MYPORT is used to set the listening port
 *
 *  [!] If using Msft, you are required to run their proprietary code before
 *      calling this routine.  This is important enough to Msft that they make
 *      it impossible to use sockets without doing so.  Doing this aids Msft in
 *      maintaining a monopoly by generating applications and code that only
 *      work on Msft platforms. It is pretty easy, albeit annoying, to sidestep
 *      this by keeping track of Msft's shenanigans (see WSAStartup).
 */
int setup() {
    int c, sock;
    struct addrinfo hints, *form;

    #ifndef _WIN32
    /*  install a signal handler so that when send attempts to write to a
     *  broken pipe and the OS sends the SIGPIPE error we can keep running
     *  instead of having our process killed off
     */
    signal(SIGPIPE, handle_sigpipe);
    #endif

    // setup the helper structure for getaddrinfo() routine
    {   memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE;
    }

    // fill out request form using a port, and previously defined hints
    if(0 != (c = getaddrinfo(NULL, MYPORT, &hints, &form))) {
        fprintf(stderr, "FATAL getaddrinfo: %s\n", gai_strerror(c)); return 0;
    }

    // request socket from operating system (OS) using completed form
    if(0 > (sock = socket(form->ai_family, form->ai_socktype, form->ai_protocol))) {
        fprintf(stderr, "FATAL socket: %s\n", strerror(errno)); return 0;
    }

    /*  [!] Deal with a minor annoyance, OSes prevent applications from using
     *  an address that was recently used and subsequently returned.  This
     *  feature is not desired - turn it off before we lock in the port portion
     *  of the address (currently the socket only has IP, no Port).
     */
    {   optType okay = 1;
        if(0 > setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &okay, sizeof(okay))) {
            fprintf(stderr, "FATAL reuse: %s\n", strerror(errno)); return 0;
        }
    }
    
    // ask OS to set the non-blocking flag on the socket
    if(0 > setNonBlocking(sock)) {
        fprintf(stderr, "FATAL non-blocking flag request: %s\n", strerror(errno));
        return 0;
    }

    // ask OS to associate our scoket with the port specified on the form
    if(0 > bind(sock, form->ai_addr /* [?] has port # */, form->ai_addrlen)) {
        fprintf(stderr, "FATAL bind: %s\n", strerror(errno)); return 0;
    }

    // discard the form, it is not used anymore
    freeaddrinfo(form);

    // notify OS that this socket is going to listen for incoming connections
    if(0 > listen(sock, BACKLOG)) {
        fprintf(stderr, "FATAL listen: %s\n", strerror(errno)); return 0;
    }

    // give the caller the server socket file descriptor
    return sock;
}
   
struct client * allocateBunnies(int sock) {

    struct client * pool = malloc (sizeof(struct client) *POOL_SIZE);
	if(NULL == pool) return NULL;
    // initialize client pool
    for(uint32_t i = 0; i < POOL_SIZE; i++) pool[i].fd = -1;
    // server listen port is first socket file descriptor
    memset(&pool[0], 0, sizeof(pool[0])); pool[0].fd = sock; 
	return pool;
}

int somethingNeedDoing(uint16_t milliseconds, struct client pool[]) {

        #ifdef USE_CLIENT_POOL
        if(!faceItMetal(1000 /* ms */, httpEcho, pool)) return softCuteBunnies(-1);
        #else
        if(!faceItMetal(sock, 1000 /* ms */, httpEcho)) return softCuteBunnies(-1);
        #endif

    return softCuteBunnies(0);
}

#ifdef _WIN32
int dumbdumbdumbdumbdumb(WSADATA *w) { return !msftVendorLockInCode(w); }
#endif
