/*****************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * $Id: multi-app.c,v 1.6 2004/10/06 13:24:08 giva Exp $
 *
 * This is an example application source code using the multi interface.
 */

#include <stdio.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>

#include <map>
/*
 * Download a HTTP file and upload an FTP file simultaneously.
 */

typedef std::map<CURL*,int> CurlHandlesMap_t;

#define HANDLECOUNT 5000   /* Number of simultaneous transfers */
#define HTTP_HANDLE 0   /* Index for the HTTP transfer */
#define FTP_HANDLE 1    /* Index for the FTP transfer */

//CURL *handles[HANDLECOUNT];
CurlHandlesMap_t handles;
CURLM *multi_handle;
int  total_bytes_received = 0;

// Generic callback handler.
size_t GenericHandler(
    void*   pBuffer, 
    size_t  sizeBufItem, 
    size_t  sizeItemsNo, 
    void*   pUserData)
{
    printf("%u > handle=0x%08x received %d bytes of response\n",(unsigned int)time(NULL),pUserData,(sizeBufItem * sizeItemsNo));
    total_bytes_received += (sizeBufItem * sizeItemsNo);
    handles[(CURL*)pUserData] += (sizeBufItem * sizeItemsNo);
    return (sizeBufItem * sizeItemsNo);
}

void CheckProgress()
{
  CURLMsg *msg; /* for picking up messages with the transfer status */
  int msgs_left; /* how many messages are left */
  int i = 0;

  /* See how the transfers went */
  while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
    if (msg->msg == CURLMSG_DONE) {

       //int idx, found = 0;

       /* Find out which handle this message is about */
       //for (idx=0; (!found && (idx<HANDLECOUNT)); idx++) found = (msg->easy_handle == handles[idx]);
       printf("%u > handle=0x%08x HTTP transfer completed with status %d msgs_left=%d\n", 
           (unsigned int)time(NULL), msg->easy_handle, msg->data.result, msgs_left);
       curl_multi_remove_handle(multi_handle, msg->easy_handle);
       if (msg->data.result == CURLE_OK)
       {
           printf("%u > handle=0x%08x total bytes received=%d\n", 
               (unsigned int)time(NULL), msg->easy_handle, handles[msg->easy_handle]);
           curl_easy_cleanup(msg->easy_handle);
           handles.erase(msg->easy_handle);
       }
       else
       {
           printf("%u > handle=0x%08x restarting transfer\n", (unsigned int)time(NULL),msg->easy_handle);
           curl_multi_add_handle(multi_handle, msg->easy_handle);
       }
    }
  }
}

int main(int argc, char **argv)
{
  int still_running; /* keep number of running handles */
  int i;
  CurlHandlesMap_t::iterator it;

  int numRequests = atoi(argv[1]);
  int numMaxConnections = atoi(argv[2]);
  
//#define THE_URL "http://127.0.0.1/fcgi-bin/SimWS.fcgi?cmd=getvegregstatus&vegid=1234"
//#define THE_URL "http://172.16.1.8"
#define THE_URL   "http://172.16.1.99:8080/quickselect/index.htm?pid=%d&counter=%d"
//#define THE_URL "http://www.ynet.co.il"
//#define THE_URL "http://172.16.1.212/index.cgi"
//#define THE_URL "http://172.16.1.236/login.aspx"

  printf("curl version: %s\n",(char*)curl_version());


  /* Allocate one CURL handle per transfer */
  for (i=0; i<numRequests; i++)
  {
      CURL* pCurl = curl_easy_init();
      handles[pCurl] = 0;
      char szReq[1000];
      sprintf(szReq,THE_URL,getpid(),i);
      /* set the options (I left out a few, you'll get the point anyway) */
      curl_easy_setopt(pCurl, CURLOPT_URL, szReq);
      curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, GenericHandler);
      curl_easy_setopt(pCurl, CURLOPT_FILE, pCurl);
  }

  /* init a multi stack */
  multi_handle = curl_multi_init();

  curl_multi_setopt(multi_handle,CURLMOPT_MAXCONNECTS,numMaxConnections);
  curl_multi_setopt(multi_handle,CURLMOPT_PIPELINING,1);
  
  /* add the individual transfers */
  for (it = handles.begin(); it != handles.end(); ++it)
      curl_multi_add_handle(multi_handle, it->first);

  /* we start some action by calling perform right away */
  while(CURLM_CALL_MULTI_PERFORM ==
        curl_multi_perform(multi_handle, &still_running));

  while(still_running && handles.size() > 0) {
    struct timeval timeout;
    int rc; /* select() return code */

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd;

    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* set a suitable timeout to play around with */
    timeout.tv_sec = 0;
    timeout.tv_usec = 5*1000;

    /* get file descriptors from the transfers */
    curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    switch(rc) {
    case -1:
      /* select error */
      printf("%u > failed to select fdset\n",(unsigned int)time(NULL));
      break;
    case 0:
      /* timeout, do something else */
      //printf("%u > timeout\n",(unsigned int)time(NULL));
      CheckProgress();
      break;
    default:
      /* one or more of curl's file descriptors say there's data to read
         or write */
      CheckProgress();
      while(CURLM_CALL_MULTI_PERFORM ==
            curl_multi_perform(multi_handle, &still_running));
      break;
    }
  }

  CheckProgress();

  curl_multi_cleanup(multi_handle);

  /* Free the CURL handles */
/*  
  for (it = handles.begin(); it != handles.end(); ++it)
  {
      if (it->first)
      {
          curl_easy_cleanup(it->first);
      }
  }
  handles.clear();
*/
  printf("%u > total_bytes_received=%d\n",(unsigned int)time(NULL),total_bytes_received);
  return 0;
}
