#ifndef LIBCURL_H
#define LIBCURL_H

#include <string>
#include <iostream>
#include <curl/curl.h>

#include <QDebug>

#include "networkagent.h"
#include "libcurl.h"

static char error_buffer[CURL_ERROR_SIZE];
static size_t writer(char *, size_t, size_t, std::string *);
static bool init(CURL *&, const char *, std::string *);

static bool init(CURL *&conn, const char *url, std::string *p_buffer)
{
    CURLcode code;

    conn = curl_easy_init();
    if (conn == Q_NULLPTR) {
        qWarning() <<  " Failed to create CURL connection";
        return false;
    }

    code = curl_easy_setopt(conn, CURLOPT_ERRORBUFFER, error_buffer);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set error buffer " << code;
        return false;
    }

#ifdef Q_OS_WIN
    code = curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, FALSE);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set ssl verifypeer" << code;
        return false;
    }
#endif

    code = curl_easy_setopt(conn, CURLOPT_URL, url);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set URL " << error_buffer;
        return false;
    }

    code = curl_easy_setopt(conn, CURLOPT_FOLLOWLOCATION, 1);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set redirect option " << error_buffer;
        return false;
    }

    code = curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, writer);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set writer " << error_buffer;
        return false;
    }

    code = curl_easy_setopt(conn, CURLOPT_WRITEDATA, p_buffer);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set write data " << error_buffer;
        return false;
    }

    code = curl_easy_setopt(conn, CURLOPT_CONNECTTIMEOUT, 10);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set timeout " << error_buffer;
        return false;
    }

    return true;
}

static size_t writer(char *data, size_t size, size_t nmemb, std::string *writer_data)
{
    size_t sizes = size * nmemb;

    if (nullptr == writer_data) {
        return 0;
    }

    writer_data->append(data, sizes);

    return sizes;
}

int libcurl_get(const char *url, std::string &buffer, std::string &errinfo)
{
    CURL *conn = nullptr;
    CURLcode code;

    curl_global_init(CURL_GLOBAL_DEFAULT);

    if (!init(conn, url, &buffer)) {
        qWarning() << " Connection initializion failed";
        errinfo = "Connection initializion failed\n";

        return -1;
    }

    code = curl_easy_perform(conn);

    if (code != CURLE_OK) {
        qWarning() << " Failed to get" << url  << error_buffer;

        errinfo.append("Failed to get ");
        errinfo.append(url);

        return -2;
    }

    curl_easy_cleanup(conn);

    return 1;
}

int libcurl_post(const char *url,
                 std::map<std::string, std::string> headers,
                 const char *data,
                 std::string &buffer)
{
    int errcode = -1;
    CURL *conn = nullptr;
    CURLcode code;
    long http_code = 0;

//    code = curl_global_init(CURL_GLOBAL_DEFAULT);

    if (!init(conn, url, &buffer)) {
        qWarning() << " Connection initializion failed";
        return CURLE_FAILED_INIT;
    }

    curl_slist *chunk = NetworkAgent::instance()->customCurlHeader();
    for (auto &v : headers) {
        chunk = curl_slist_append(chunk, (v.first + ": " + v.second).data());
    }
    /* set our custom set of headers */
    code = curl_easy_setopt(conn, CURLOPT_HTTPHEADER, chunk);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set CURLOPT_POST " << error_buffer;
        goto clean_curl_post;
    }

    code = curl_easy_setopt(conn, CURLOPT_POST, true);

    if (code != CURLE_OK) {
        qWarning() << " Failed to set CURLOPT_POST " << error_buffer;
        goto clean_curl_post;
    }

    code = curl_easy_setopt(conn, CURLOPT_POSTFIELDS, data);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set CURLOPT_POSTFIELDS " << error_buffer;
        goto clean_curl_post;
    }

    code = curl_easy_perform(conn);
    if (code != CURLE_OK) {
        qWarning() << " Failed to set curl_easy_perform " << error_buffer;
        goto clean_curl_post;
    }

    code = curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
    if (http_code == 200 && code != CURLE_ABORTED_BY_CALLBACK) {
        //Succeeded
        errcode = 0;
    }

    qDebug() << "http_code:" << http_code;

clean_curl_post:
    curl_easy_cleanup(conn);
    curl_slist_free_all(chunk);
    return errcode;
}


int libcurl_post(const char *url, const char *data, std::string &buffer)
{
    return libcurl_post(url, std::map<std::string, std::string>(), data, buffer);
};

#endif // LIBCURL_H
