/*   -*- coding: utf-8 -*-  */

/*                                     Libcurl interface */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Nlm.h"
#include <curl/curl.h>
#include "CharSetBuffer.h"

int NlmTraceMode = 0;
CURL *NlmCurlHandle;

static Buffer *bufUrlQuery=NULL;

char *NlmComputeUrlSearch (char *db, char *query, int size)
{
  if (!bufUrlQuery) bufUrlQuery=NewBuffer();
  BufferStrcpy(bufUrlQuery, "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=");
  BufferStrcat(bufUrlQuery, db);
  BufferStrcat(bufUrlQuery, "&term=");
  CharSetUrlBufferCatFromStr(bufUrlQuery, query);
  BufferStrcat(bufUrlQuery, "&retmax=");
  BufferIntCat(bufUrlQuery, size);
  return BufferString(bufUrlQuery);
}

char *NlmComputeUrlFetch (char *db, char *idList)
{
  if (!bufUrlQuery) bufUrlQuery=NewBuffer();
  BufferStrcpy(bufUrlQuery, "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=");
  BufferStrcat(bufUrlQuery, db);
  BufferStrcat(bufUrlQuery, "&id=");
  BufferStrcat(bufUrlQuery, idList);
  BufferStrcat(bufUrlQuery, "&retmode=xml");
  return BufferString(bufUrlQuery);
}


static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
 
  mem->memory = realloc(mem->memory, mem->size + realsize + 1);
  if(mem->memory == NULL) {
    /* out of memory! */ 
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }
 
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;
 
  return realsize;
}

struct MemoryStruct NlmChunk;

void NlmCurlInit()
{
  NlmChunk.memory = malloc(1);  /* will be grown as needed by the realloc above */ 
  NlmChunk.size = 0;    /* no data at this point */ 
  curl_global_init(CURL_GLOBAL_ALL);
  NlmCurlHandle = curl_easy_init();
}

void NlmCurlEnd()
{
  curl_easy_cleanup(NlmCurlHandle);
}

void NlmPerformFetch(char *base, char *idList)
{
  char *urlFetch;
  CURLcode resCurl;

  if (NlmTraceMode > 0)
    {
      printf("%s\n",idList);
    }
  urlFetch=NlmComputeUrlFetch (base, idList);
  if (NlmTraceMode > 0)
    {
      printf("%s\n",urlFetch);
    }
  curl_easy_reset(NlmCurlHandle);
  curl_easy_setopt(NlmCurlHandle, CURLOPT_URL, urlFetch);
  resCurl = curl_easy_perform(NlmCurlHandle);
  if(resCurl != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
	    curl_easy_strerror(resCurl));
}

SxmlNode *NlmPerformSearch(char *db, char *query, int size)
{
  char *urlQuery;
  CURLcode resCurl;
  SxmlNode *resultDoc;

  urlQuery=NlmComputeUrlSearch (db, query, size);

  curl_easy_setopt(NlmCurlHandle, CURLOPT_URL, urlQuery);
  curl_easy_setopt(NlmCurlHandle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  curl_easy_setopt(NlmCurlHandle, CURLOPT_WRITEDATA, (void *)&NlmChunk);
  
  /* Perform the request, res will get the return code */ 
  resCurl = curl_easy_perform(NlmCurlHandle);
  /* Check for errors */ 
  if(resCurl != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
	    curl_easy_strerror(resCurl));
  if (NlmTraceMode > 0)
    {
      printf("%lu bytes retrieved\n", (long)NlmChunk.size);
      printf("%s\n", NlmChunk.memory);
    }
  resultDoc=SxmlDocumentFromString(NlmChunk.memory);
  return resultDoc;
}
