#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include <curl/curl.h>

#define DEBUG_CALLBACK2(fmt,param1,param2) sprintf(tmpBuffer, fmt, param1, param2);\
  debug_callback(tmpBuffer, file_handler);
#define DEBUG_CALLBACK3(fmt,param1,param2,param3) sprintf(tmpBuffer, fmt, param1, param2, param3);\
  debug_callback(tmpBuffer, file_handler);
#define DEBUG_CALLBACK4(fmt,param1,param2,param3,param4) sprintf(tmpBuffer, fmt, param1, param2, param3, param4);\
  debug_callback(tmpBuffer, file_handler);

#define MAX_POST_BUFFER 2047
#define MAX_THREAD 100
pthread_mutex_t mut;

void debug_callback
(
  char            *strBuffer,
  void            *stream
)
{
  if (stream)
  {
    fprintf(stream, "%s\n", strBuffer);
  }
}

struct MemoryStruct {
  char *memory;
  size_t size;
};

size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
  register int realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)data;

  mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
  if (mem->memory) {
    memcpy(&(mem->memory[mem->size]), ptr, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;
  }
  return realsize;
}

struct CallbackInfoStruct {
  void (*debug_callback)(char *strBuffer, void *stream);
  void *stream;
};

static int MyTrace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, void *userp)
{
  char tmpBuffer[MAX_POST_BUFFER + 1] = {'\0'};
  struct CallbackInfoStruct *callback_info = (struct CallbackInfoStruct *)userp;

  (void)handle; /* prevent compiler warning */

  if (callback_info->debug_callback)
  {
    switch (type)
    {
      case CURLINFO_TEXT:
        strncpy(tmpBuffer, "== Info: ", MAX_POST_BUFFER + 1);
        strncat(tmpBuffer, (const char *)data, MAX_POST_BUFFER + 1);
        tmpBuffer[MAX_POST_BUFFER] = '\0';
        break;
      case CURLINFO_HEADER_OUT:
        strncpy(tmpBuffer, "=> Send header", MAX_POST_BUFFER + 1);
        tmpBuffer[MAX_POST_BUFFER] = '\0';
        break;
      case CURLINFO_DATA_OUT:
        strncpy(tmpBuffer, "=> Send data", MAX_POST_BUFFER + 1);
        tmpBuffer[MAX_POST_BUFFER] = '\0';
        break;
      case CURLINFO_HEADER_IN:
        strncpy(tmpBuffer, "<= Recv header", MAX_POST_BUFFER + 1);
        tmpBuffer[MAX_POST_BUFFER] = '\0';
        break;
      case CURLINFO_DATA_IN:
        strncpy(tmpBuffer, "<= Recv data", MAX_POST_BUFFER + 1);
        tmpBuffer[MAX_POST_BUFFER] = '\0';
        break;
      default: /* in case a new one is introduced to shock us */
        return 0;
    }

    callback_info->debug_callback(tmpBuffer, callback_info->stream);
  }

  return 0;
}

void *runner(void *param)
{
  char tmpBuffer[MAX_POST_BUFFER + 1] = {'\0'};
  int return_code = 0;
  FILE *file_handler;
  char buffer[255 + 1] = {""};
  const char *postBuffer = {
         "x_ADC_Delim_Character=,&x_ADC_Delim_Data=True"};

  CURL *curl = NULL;
  CURLcode res;

  struct MemoryStruct chunk; /* Returned data */
  struct CallbackInfoStruct callback_info;
  char error_buffer[CURL_ERROR_SIZE] = {""};

  char an_url[255 + 1] = {"http://smspc19/CCTran/SimAuthorizeNet_2.php"}; /* CHANGE ME */

  /*
   * Init
   */
  chunk.memory = NULL; /* we expect realloc(NULL, size) to work */
  chunk.size = 0;      /* no data at this point */

  sprintf(buffer, "/tmp/test_mt_cc_tran_%p", pthread_self());
  file_handler = fopen(buffer, "w");
  if (file_handler == NULL)
  {
    file_handler = stderr;
  }

  callback_info.debug_callback = debug_callback;
  callback_info.stream = file_handler;

  /*
   * Main
   */
  curl = curl_easy_init();
  if (curl) {
    /* what call to write: */
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1 );
    curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, MyTrace);
    curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &callback_info);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
    curl_easy_setopt(curl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0L);
    /* send all data to this function  */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    /* we pass our 'chunk' struct to the callback function */
    curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_URL, an_url);

    while(1)                    /* do some ugly short cut... */
    {
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postBuffer); /* must be text data without using CURLOPT_POSTFIELDSIZE */
      curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); /* curl will find size itself */

      DEBUG_CALLBACK2("%s(%d) curl_easy_perform", __FILE__, __LINE__);
      /* pthread_mutex_lock(&mut); */
      res = curl_easy_perform(curl);
      /* pthread_mutex_unlock(&mut); */
      if (res != 0)
      {
        DEBUG_CALLBACK3("%s(%d) curl_easy_perform res = %d", __FILE__, __LINE__, res);
        DEBUG_CALLBACK3("%s(%d) error_buffer = %s", __FILE__, __LINE__, error_buffer);
      }
      DEBUG_CALLBACK2("%s(%d) curl_easy_perform end", __FILE__, __LINE__);

      break;                   /* we are done... */
    }
    /* always cleanup */
    curl_easy_cleanup(curl);

    DEBUG_CALLBACK3("%s(%d) chunk.size = %lu", __FILE__, __LINE__, chunk.size);
    DEBUG_CALLBACK3("%s(%d) chunk.memory = %s", __FILE__, __LINE__, chunk.memory);
    free( chunk.memory ); chunk.memory = 0;
    chunk.size = 0;
  }
  else
  {
    DEBUG_CALLBACK2("%s(%d) Fail of curl_easy_init", __FILE__, __LINE__);
  }

  pthread_exit(0);
  return param;
}

int main(int argc, char **argv)
{
  pthread_t tid[MAX_THREAD];
  pthread_attr_t attr;
  int i;

  fprintf(stderr, "%s(%d) curl version %s\n", __FILE__, __LINE__, curl_version());
  fprintf(stderr, "%s(%d) Thread number = %d\n", __FILE__, __LINE__, MAX_THREAD);

  curl_global_init(CURL_GLOBAL_DEFAULT);

  pthread_attr_init(&attr);
  pthread_mutex_init(&mut,NULL);

  for (i = 0; i < MAX_THREAD; i++)
  {
    pthread_create( &tid[i], &attr, runner, NULL);
  }

  for (i = 0; i < MAX_THREAD; i++)
  {
    pthread_join( tid[i], NULL);  /* makes the main code wait for all the threads */
  }

  curl_global_cleanup();
}

