#include <stdio.h>
#include <string>
#include <assert.h>

#define CURL_STATICLIB
#include "curl/curl.h"
#include "openssl/sha.h"

#include "vn_json.h"

static size_t http_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{ 
    size_t realsize = size * nmemb;
    
    std::string *data = (std::string*)userp;

    data->append((char*)contents, realsize);

    return realsize;
}

/* lock callback */
static void my_lock(CURL *handle, curl_lock_data data, curl_lock_access laccess,
          void *useptr )
{
  const char *what;
  (void)handle;
  (void)laccess;

  switch ( data ) {
    case CURL_LOCK_DATA_SHARE:
      what = "share";
      break;
    case CURL_LOCK_DATA_DNS:
      what = "dns";
      break;
    case CURL_LOCK_DATA_COOKIE:
      what = "cookie";
      break;
    default:
      fprintf(stderr, "lock: no such data: %d\n", (int)data);
      return;
  }
  printf("--------- lock:   %-6s\n", what);
}

/* unlock callback */
static void my_unlock(CURL *handle, curl_lock_data data, void *useptr )
{
  const char *what;
  (void)handle;
  (void)useptr;
  switch ( data ) {
    case CURL_LOCK_DATA_SHARE:
      what = "share";
      break;
    case CURL_LOCK_DATA_DNS:
      what = "dns";
      break;
    case CURL_LOCK_DATA_COOKIE:
      what = "cookie";
      break;
    default:
      fprintf(stderr, "unlock: no such data: %d\n", (int)data);
      return;
  }
  printf("------------ unlock: %-6s\n", what);
}



int main(int argc, char *argv[])
{
    CURL *curl_handle;
    CURLcode res;

    std::string data;

    curl_global_init(CURL_GLOBAL_ALL);

    CURLSH *curl_share = curl_share_init();
    // curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, my_lock);
    // curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, my_unlock);
     curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);

    /* init the curl session */
    curl_handle = curl_easy_init();


//    curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 3);
//    curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);

    /* specify URL to get */
    curl_easy_setopt(curl_handle, CURLOPT_URL, "http://207.207.163.176/api/call/getLoginInfo");
    
    /* send all data to this function  */
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, http_write_callback);
    
    /* we pass our 'chunk' struct to the callback function */
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&data);
    
    /* some servers don't like requests that are made without a user-agent
       field, so we provide one */
    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); 

    curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, ""); /* just to start the cookie engine */

    curl_easy_setopt(curl_handle, CURLOPT_SHARE, curl_share);

    /* get it! */
    res = curl_easy_perform(curl_handle);

    /* check for errors */
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(res));
    }
    else {
        /*
         * Now, our chunk.memory points to a memory block that is chunk.size
         * bytes big and contains the remote file.
         *
         * Do something nice with it!
         *
         * You should be aware of the fact that at this point we might have an
         * allocated data block, and nothing has yet deallocated that data. So when
         * you're done with it, you should free() it as a nice application.
         */

        printf("%lu bytes retrieved\n", data.size());
        printf("\n%s\n", data.c_str());
        
/////////////////// Cookies /////////////////////////////////////
        struct curl_slist *cookies, *nc;
        int i;
        char token[128] = {0};
        
        printf("Cookies, curl knows:\n");
        res = curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies);
        if (res != CURLE_OK) {
            fprintf(stderr, "Curl curl_easy_getinfo failed: %s\n", curl_easy_strerror(res));
            exit(1);
        }
        nc = cookies, i = 1;
        while (nc) {
            printf("[%d]: %s\n", i, nc->data);

            sscanf(nc->data,  "%*s\t%*s\t%*s\t%*s\t%*lu\ttoken\t%s", token);

            nc = nc->next;
            i++;
        }
        if (i == 1) {
            printf("(none)\n");
        }
        curl_slist_free_all(cookies);
        printf("!! token = %s\n", token);
/////////////////////////////////////////////////////////////////
        
        // parse json
        vn_json_t *root = vn_json_parse(data.c_str());
        assert(root);

        vn_json_t *js = vn_json_get_object_item(root, "loginInfo");
        assert(js);

        js = vn_json_get_object_item(js, "encryptionKey");
        assert(js);

        printf("key: %s\n", vn_json_get_string(js));

        ////////
        /////// sha512(${encryptionKey} + sha512(${password}) + ${encryptionKey})

        unsigned char digest[SHA512_DIGEST_LENGTH];
        
        SHA512((const unsigned char*)"topse", 5, (unsigned char*)&digest);    
        
        char digest_str[SHA512_DIGEST_LENGTH*2+1];
        
        for(int i = 0; i < SHA512_DIGEST_LENGTH; i++)
            sprintf(&digest_str[i*2], "%02x", (unsigned int)digest[i]);
        
        printf("passwd_digest: %s\n", digest_str);

        std::string s = std::string(vn_json_get_string(js)) + digest_str + vn_json_get_string(js);

        SHA512((const unsigned char*)s.c_str(), s.size(), (unsigned char*)&digest);
        for(int i = 0; i < SHA512_DIGEST_LENGTH; i++)
            sprintf(&digest_str[i*2], "%02x", (unsigned int)digest[i]);

        
        printf("result digest: %s\n", digest_str);


        data.clear();
        std::string post_data = (std::string("name=admin&credentials=") + digest_str);
        post_data += std::string("&token=") + token; 

        // HACK:
        curl_handle = curl_easy_init();

        curl_easy_setopt(curl_handle, CURLOPT_URL, "http://207.207.163.176/api/call/login");                   
        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, http_write_callback);
        curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&data);
        curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
        curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, post_data.c_str());
        curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, post_data.size());
        curl_easy_setopt(curl_handle, CURLOPT_SHARE, curl_share);

        {
/////////////////// Cookies /////////////////////////////////////
        struct curl_slist *cookies, *nc;
        int i;
        char token[128] = {0};
        
        printf("Cookies, curl knows:\n");
        res = curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies);
        if (res != CURLE_OK) {
            fprintf(stderr, "Curl curl_easy_getinfo failed: %s\n", curl_easy_strerror(res));
            exit(1);
        }
        nc = cookies, i = 1;
        while (nc) {
            printf("[%d]: %s\n", i, nc->data);

            sscanf(nc->data,  "%*s\t%*s\t%*s\t%*s\t%*lu\ttoken\t%s", token);

            nc = nc->next;
            i++;
        }
        if (i == 1) {
            printf("(none)\n");
        }
        curl_slist_free_all(cookies);
        printf("!! token = %s\n", token);
/////////////////////////////////////////////////////////////////
        }

        /* get it! */
        res = curl_easy_perform(curl_handle);
        
        /* check for errors */
        if(res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
            exit(1);
        }

         printf("%lu bytes retrieved\n", data.size());
        printf("\n%s\n", data.c_str());

        
        vn_json_destroy(root);

    }
    
    /* cleanup curl stuff */
    curl_easy_cleanup(curl_handle);


    /* we're done with libcurl, so clean it up */
    curl_global_cleanup();

    return 0;
}
