/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
/* This is an example application source code using multi interface & http/2 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>
#define MIN_REQ_CURL_VER 0x072100 /* 7.33.0, for HTTP2 support */

/* Number of requests to make */
#define NUM_REQUESTS 5

typedef struct {
    char*   msg;
    char*   source_uri;
    int     len;
} CurlResponse;

static size_t
curl_get_chunks(void *ptr, size_t size, size_t nmemb, CurlResponse *resp)
{
  size_t new_len = resp->len + size*nmemb;
    printf("Received curl chunk for URI %s of size %d, new total size %d\n",
            resp->source_uri, (int) (size*nmemb), (int) new_len);

  resp->msg = realloc(resp->msg, (new_len + 1) * sizeof(char));
  if (resp->msg == NULL) {
    fprintf(stderr, "Realloc for cURL response message failed!\n");
    return 0;
  }
    memcpy(resp->msg+resp->len, ptr, size*nmemb);
    resp->len = new_len;
  return size*nmemb;
}

/*
 * Download the same HTTP file multiple times!
 */

#define HANDLECOUNT 5 /* Numner of http requests to make */
int main(int argc, char* argv[])
{
  CURL *handles[HANDLECOUNT];
  CurlResponse resps[HANDLECOUNT];
  CURLM *multi_handle;
  curl_version_info_data* curlver;

  int still_running; /* keep number of running handles */
  int i;
  int rc_ok = 0;
    

  if(argc != 2) {
    fprintf(stderr, "[%s]: USAGE: %s <url1>\n", argv[0], argv[0]);
    return -1;
  }

  curlver = curl_version_info(CURLVERSION_NOW);
  if(curlver->version_num < MIN_REQ_CURL_VER) {
    fprintf(stderr, "Version of libcURL loaded is %s it is too old!", curlver->version);
  }

  /* init a multi stack */
  multi_handle = curl_multi_init();

  curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 1);
  curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, 1);


  for (i=0; i<HANDLECOUNT; i++)
  {
    handles[i] = curl_easy_init();
    resps[i].msg  = NULL;
    resps[i].len = 0;
    resps[i].source_uri = argv[1];
    /* set options */
    curl_easy_setopt(handles[i], CURLOPT_URL, resps[i].source_uri);
    curl_easy_setopt(handles[i], CURLOPT_WRITEFUNCTION, curl_get_chunks);
    curl_easy_setopt(handles[i], CURLOPT_WRITEDATA, &resps[i]);
    if(curlver->features && CURL_VERSION_HTTP2)
      curl_easy_setopt(handles[i], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
  }

  struct timeval timeout;
  int rc; /* select() return code */

  fd_set fdread;
  fd_set fdwrite;
  fd_set fdexcep;
  int maxfd = -1;

  long curl_timeo = -1;

  FD_ZERO(&fdread);
  FD_ZERO(&fdwrite);
  FD_ZERO(&fdexcep);

  /* set a suitable timeout to play around with */
  timeout.tv_sec = 1;
  timeout.tv_usec = 0;

  curl_multi_timeout(multi_handle, &curl_timeo);
  if(curl_timeo >= 0) 
  {
  timeout.tv_sec = curl_timeo / 1000;
  if(timeout.tv_sec > 1)
    timeout.tv_sec = 1;
  else
    timeout.tv_usec = (curl_timeo % 1000) * 1000;
  }

  // get file descriptors from the transfers
  curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

  /* In a real-world program you OF COURSE check the return code of the
  function calls.  On success, the value of maxfd is guaranteed to be
  greater or equal than -1.  We call select(maxfd + 1, ...), specially in
  case of (maxfd == -1), we call select(0, ...), which is basically equal
  to sleep. */

  rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

  switch(rc) {
  case -1:
  /* select error */
  break;
  case 0:
  default:
  /* timeout or readable/writable sockets */
  //curl_multi_perform(multi_handle, &still_running);
    rc_ok = 1;
  break;
  }
  
  if (rc_ok != 0)
  {
	/* Make a initial request in order to perform h2c upgrade */
    curl_multi_add_handle(multi_handle, handles[0]);
    curl_multi_perform(multi_handle, &still_running);
  }
  else return -1;


  for(i=1; i<NUM_REQUESTS; i++) 
  {
    curl_multi_add_handle(multi_handle, handles[i]);
  }

  do
  {
     curl_multi_perform(multi_handle, &still_running);
  } while (still_running);
  
  int exit =0;
  int k = 0;
  CURLMsg *curl_message;
  
  while (exit == 0)
  {
    curl_message = curl_multi_info_read(multi_handle, &k);
    if(curl_message == NULL) 
    {
      exit = 1;
    }
    else if(curl_message->msg == CURLMSG_DONE) 
    {
      if (curl_message->easy_handle == NULL) 
      {
        fprintf(stderr, "easy_handle NULL but should not have been\n"); /* observed this sometimes*/
        break;
      }
      else 
      {  
        fprintf(stderr, "Response received\n");
        curl_multi_remove_handle(multi_handle, curl_message->easy_handle);
      }
    }
    else
    {
      fprintf(stderr, "Other msg returned %u\n", curl_message->msg);
    }
  
  }

  curl_multi_cleanup(multi_handle);

  /* Free the CURL handles */
  for (i=0; i<HANDLECOUNT; i++)
      curl_easy_cleanup(handles[i]);

  return 0;
}
