#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <curl/curl.h>
#include <pthread.h>

#define URL "http://172.18.183.238:8080/something"


/*
 * Send 6 'Q' characters to the web server
 * For this test, the content of the body is irrelevant
 */
const unsigned char body[1024] = { 'Q', 'Q', 'Q', 'Q', 'Q', 'Q' };
const size_t body_len = 6;

#define BUFSIZE  2047

typedef struct {
    char data[BUFSIZE + 1];
    int offset;
} RESPONSE_DATA;

static void reset_response(RESPONSE_DATA *r)
{
    r->data[0] = '\0';
    r->offset = 0;
}


static void *worker(void *p);
static int tasp_handle_web(CURL *p_curl, RESPONSE_DATA *p_web_response);
static void set_curl_general_request_options( CURL *p_curl, RESPONSE_DATA *p_web_response);
static size_t write_callback( void *contents, size_t size, size_t nmemb, void *userp);


int main(int argc, char **argv)
{
    if (argc == 1) {
        fprintf(stderr, "Usage: %s <# connections>\n", argv[0]);
        exit(1);
    }

    int nconn = atoi(*++argv);

    CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
    assert (res == CURLE_OK);

    int i;
    for (i=0; i < nconn; i++)
    {
        pthread_t tid;

        CURL *p_curl = curl_easy_init();
        assert(p_curl);

        pthread_create(&tid, NULL, worker, p_curl);
    }

    pause();
}


void *worker(void *p)
{
    RESPONSE_DATA response;

    /*
     * curl_easy_init is called for each individual thread
     */
    CURL *p_curl = p;

    for (;;)
    {
        reset_response(&response);

        tasp_handle_web(p_curl, &response);
    }

    return NULL;
}


static int tasp_handle_web(CURL *p_curl, RESPONSE_DATA *p_web_response)
{
    CURLcode res;

    set_curl_general_request_options(p_curl, p_web_response);

    /*
     * PERFORM REQUEST
     *
     */
    res = curl_easy_perform(p_curl);

    if (res != CURLE_OK)
    {
        printf("Cannot send URL to web server: %d\n", res);
    }

    curl_easy_reset(p_curl);

    return 0;
}


static void set_curl_general_request_options(CURL *p_curl, RESPONSE_DATA *p_web_response)
{
    curl_easy_setopt(p_curl, CURLOPT_POST, 1L);

    curl_easy_setopt(p_curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(p_curl, CURLOPT_POSTFIELDSIZE, (long) body_len);
    curl_easy_setopt(p_curl, CURLOPT_URL, URL);

    /*
     * Crash occurs after this time
     */
    long timeout = 3;
    curl_easy_setopt(p_curl, CURLOPT_TIMEOUT, timeout);

    /*
     * return BODY stuff
     */
    curl_easy_setopt(p_curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(p_curl, CURLOPT_WRITEDATA, (void *) p_web_response);
}

static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;
    RESPONSE_DATA *p_web_response = (RESPONSE_DATA *) userp;

    int offset = p_web_response->offset;

    size_t spaceleft = BUFSIZE - offset;

    if (realsize > spaceleft)
    {
        printf("Response Callback: not enough space (have %d, need %d), "
                       "ignoring response body\n", spaceleft, realsize);

        return 0;
    }

    memcpy(&p_web_response->data[offset], contents, realsize);
    p_web_response->data[realsize] = '\0';
    p_web_response->offset += realsize;

    return realsize;
}
