/* None of the tests in here rely on a client existing, or even a network
   connection.  We should figure out how to create a test client and do
   such tests.
*/

#include "unistdx.h"
#include <stdio.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <errno.h>
#include <string.h>

#include "xmlrpc_config.h"

#include "int.h"
#include "casprintf.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/abyss.h"

#include "testtool.h"

#include "abyss.h"



static void
bindSocketToPort(int      const fd,
                 uint16_t const portNumber) {

    struct sockaddr_in name;
    int rc;

    name.sin_family = AF_INET;
    name.sin_port   = htons(portNumber);
    name.sin_addr.s_addr = INADDR_ANY;

    rc = bind(fd, (struct sockaddr *)&name, sizeof(name));
    if (rc != 0)
        fprintf(stderr, "bind() of %d failed, errno=%d (%s)",
                fd, errno, strerror(errno));

    TEST(rc == 0);
}



static void
chanSwitchCreateFd(int            const fd,
                   TChanSwitch ** const chanSwitchPP,
                   const char **  const errorP) {

#ifdef WIN32
    ChanSwitchWinCreateWinsock(fd, chanSwitchPP, errorP);
#else
    ChanSwitchUnixCreateFd(fd, chanSwitchPP, errorP);
#endif
}    



static void
closesock(int const fd) {
#ifdef WIN32
    closesocket(fd);
#else
    close(fd);
#endif
}



static void
chanSwitchCreate(uint16_t       const portNumber,
                 TChanSwitch ** const chanSwitchPP,
                 const char **  const errorP) {

#ifdef WIN32
    ChanSwitchWinCreate(portNumber, chanSwitchPP, errorP);
#else
    ChanSwitchUnixCreate(portNumber, chanSwitchPP, errorP);
#endif
}    



static void
channelCreateFd(int const fd,
                TChannel **   const channelPP,
                const char ** const errorP) {

#ifdef WIN32
    struct abyss_win_chaninfo * channelInfoP;
    ChannelWinCreateWinsock(fd, channelPP, &channelInfoP, errorP);
#else
    struct abyss_unix_chaninfo * channelInfoP;
    ChannelUnixCreateFd(fd, channelPP, &channelInfoP, errorP);
#endif
}



static void
testChanSwitchOsSocket(void) {

    XMLRPC_SOCKET rc;

    rc = socket(AF_INET, SOCK_STREAM, 0);
    if (rc < 0) {
        fprintf(stderr, "socket() failed with errno %d (%s)",
                errno, strerror(errno));
        abort();
    } else {
        int const fd = rc;

        TChanSwitch * chanSwitchP;
        TServer server;
        const char * error;

        bindSocketToPort(fd, 8080);

        chanSwitchCreateFd(fd, &chanSwitchP, &error);

        TEST_NULL_STRING(error);
        
        ServerCreateSwitch(&server, chanSwitchP, &error);
        
        TEST_NULL_STRING(error);
        
        ServerFree(&server);
        
        ChanSwitchDestroy(chanSwitchP);

        closesock(fd);
    }
}



static void
testChanSwitch(void) {

    TServer server;
    TChanSwitch * chanSwitchP;
    const char * error;

    chanSwitchCreate(8080, &chanSwitchP, &error);

    TEST_NULL_STRING(error);

    ServerCreateSwitch(&server, chanSwitchP, &error);

    TEST_NULL_STRING(error);

    ServerFree(&server);

    ChanSwitchDestroy(chanSwitchP);
    
    testChanSwitchOsSocket();
}



static void
testChannel(void) {

    XMLRPC_SOCKET rc;

    rc = socket(AF_INET, SOCK_STREAM, 0);
    if (rc < 0) {
        fprintf(stderr, "socket() failed with errno %d (%s)",
                errno, strerror(errno));
        abort();
    } else {
        int const fd = rc;

        TChannel * channelP;
        const char * error;

        channelCreateFd(fd, &channelP, &error);

        TEST(error);

        TEST(strstr(error, "not in connected"));

        strfree(error);
    }
}



static void
testOsSocket(void) {

    XMLRPC_SOCKET rc;

    rc = socket(AF_INET, SOCK_STREAM, 0);
    if (rc < 0) {
        fprintf(stderr, "socket() failed with errno %d (%s)",
                errno, strerror(errno));
        abort();
    } else {
        int const fd = rc;

        TServer server;
        abyss_bool success;

        bindSocketToPort(fd, 8080);

        success = ServerCreateSocket(&server, NULL, fd, NULL, NULL);

        TEST(success);

        ServerFree(&server);

        closesock(fd);
    }
}



static void
testSocket(void) {

#ifndef WIN32
    int rc;

    rc = socket(AF_INET, SOCK_STREAM, 0);
    if (rc < 0) {
        fprintf(stderr, "socket() failed with errno %d (%s)",
                errno, strerror(errno));
        abort();
    } else {
        int const fd = rc;

        TSocket * socketP;
        TServer server;
        const char * error;

        SocketUnixCreateFd(fd, &socketP);

        TEST(socketP != NULL);

        ServerCreateSocket2(&server, socketP, &error);

        TEST(!error);

        ServerFree(&server);

        SocketDestroy(socketP);

        close(fd);
    }
#endif
}



static void
testServerCreate(void) {

    TServer server;
    abyss_bool success;

    success = ServerCreate(&server, NULL, 8080, NULL, NULL);
    TEST(success);
    ServerInit(&server);
    ServerFree(&server);

    success = ServerCreate(&server, "myserver", 8080,
                           "/tmp/docroot", "/tmp/logfile");
    TEST(success);
    ServerInit(&server);
    ServerFree(&server);

    success = ServerCreateNoAccept(&server, NULL, NULL, NULL);
    TEST(success);
    ServerFree(&server);

    {
        TChanSwitch * chanSwitchP;
        const char * error;

        chanSwitchCreate(8080, &chanSwitchP, &error);
        
        TEST_NULL_STRING(error);

        ServerCreateSwitch(&server, chanSwitchP, &error);
        
        TEST_NULL_STRING(error);

        ServerSetName(&server, "/tmp/docroot");
        ServerSetLogFileName(&server, "/tmp/logfile");
        ServerSetKeepaliveTimeout(&server, 50);
        ServerSetKeepaliveMaxConn(&server, 5);
        ServerSetTimeout(&server, 75);
        ServerSetAdvertise(&server, 1);
        ServerSetAdvertise(&server, 0);

        ServerInit(&server);

        ServerFree(&server);

        ChanSwitchDestroy(chanSwitchP);
    }        
}



void
test_abyss(void) {

    const char * error;

    printf("Running Abyss server tests...\n");
    
    AbyssInit(&error);
    TEST_NULL_STRING(error);

    ChanSwitchInit(&error);
    TEST_NULL_STRING(error);

    ChannelInit(&error);
    TEST_NULL_STRING(error);

    testChanSwitch();

    testChannel();

    testOsSocket();

    testSocket();

    testServerCreate();

    ChannelTerm();
    ChanSwitchTerm();
    AbyssTerm();

    printf("\n");
    printf("Abyss server tests done.\n");
}

