
#include "curl_fct.h"
#include <pthread.h>

#define COOKIE_FILE "temp.cookie"

#define SSL 0

CURL *get_c;
CURL *put_c;
CURL *get_events_c;
CURL *share_c;

pthread_mutex_t cookie_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t share_mutex = PTHREAD_MUTEX_INITIALIZER;
#if SSL == 1
pthread_mutex_t ssl_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

char *get_buf=NULL;
char *get_events_buf=NULL;

struct CBC *get_cbc=NULL;
struct CBC *put_cbc=NULL;
struct CBC_events *get_events_cbc=NULL;

static
void share_lock_function(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
{

  switch ( data ) {
    case CURL_LOCK_DATA_SHARE:
      pthread_mutex_lock(&share_mutex);
      break;
    case CURL_LOCK_DATA_DNS:
      //pthread_mutex_lock(&dns_mutex);
      break;
    case CURL_LOCK_DATA_COOKIE:
      pthread_mutex_lock(&cookie_mutex);
      break;
#if SSL == 1
    case CURL_LOCK_DATA_SSL_SESSION:
      pthread_mutex_lock(&ssl_mutex);
      break;
#endif
    default:
      fprintf(stderr, "lock: no such data: %d\n", (int)data);
      return;
  }

}

static 
void share_unlock_function(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
{

  switch ( data ) {
    case CURL_LOCK_DATA_SHARE:
      pthread_mutex_unlock(&share_mutex);
      break;
    case CURL_LOCK_DATA_DNS:
      //pthread_mutex_unlock(&dns_mutex);
      break;
    case CURL_LOCK_DATA_COOKIE:
      pthread_mutex_unlock(&cookie_mutex);
      break;
#if SSL == 1
    case CURL_LOCK_DATA_SSL_SESSION:
      pthread_mutex_unlock(&ssl_mutex);
      break;
#endif
    default:
      fprintf(stderr, "lock: no such data: %d\n", (int)data);
      return;
  }
	
}

static size_t putBuffer(void *ptr, size_t size, size_t nmemb, void *cbx)
{
	struct CBC *cbc = cbx;
	
	int length = cbc->size - cbc->pos;
	if(length > size * nmemb)
	{
		printf("Read_callback : Data to long\n");
		length = size*nmemb; 
	}
	memcpy(ptr, &cbc->buf[cbc->pos], length);
	cbc->pos += length;
	return length;
}

static size_t
copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
{
	struct CBC *cbc = ctx;

	printf("%s\n", (char*)ptr);

	if (cbc->pos + size * nmemb > cbc->size)
		return 0;                   /* overflow */
	memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
	cbc->pos += size * nmemb;
	return size * nmemb;
}

static size_t
copyBufferEvents (void *ptr, size_t size, size_t nmemb, void *ctx)
{
	struct CBC_events *cbc = ctx;

	printf("%s\n", (char*)ptr);

	if (cbc->pos + size * nmemb > cbc->size)
	{
		cbc->pos = 0;                   /* overflow */
	}
	cbc->e((char*)ptr);
	//memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
	//cbc->pos += size * nmemb;
	return size * nmemb;
}

int init_curl(EventFunction _e)
{
	share_c = curl_share_init();
	get_c = curl_easy_init();
	put_c = curl_easy_init();
	get_events_c = curl_easy_init();

	if( !get_c || !put_c || !get_events_c )
		return -1;

	get_buf = (char*)malloc(2048*sizeof(char));
	get_events_buf = (char*)malloc(2048*sizeof(char));

	if( !get_buf || !get_events_buf )
		return -1;

	get_cbc = (struct CBC*)malloc(sizeof(struct CBC));
	put_cbc = (struct CBC*)malloc(sizeof(struct CBC));
	get_events_cbc = (struct CBC_events*)malloc(sizeof(struct CBC_events));

	get_cbc->buf = get_buf;
	get_cbc->size = 2048;
	get_cbc->pos = 0;

	put_cbc->buf = NULL;
	put_cbc->size = 0;
	put_cbc->pos = 0;

	get_events_cbc->buf = get_events_buf;
	get_events_cbc->size = 2048;
	get_events_cbc->pos = 0;
	get_events_cbc->e = _e;

	curl_share_setopt(share_c, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
#if SSL == 1
	curl_share_setopt(share_c, CURLOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
#endif
	curl_share_setopt(share_c, CURLSHOPT_LOCKFUNC, &share_lock_function);
	curl_share_setopt(share_c, CURLSHOPT_UNLOCKFUNC, &share_unlock_function);

	curl_easy_setopt (get_c, CURLOPT_WRITEFUNCTION, &copyBuffer);
	curl_easy_setopt (get_c, CURLOPT_WRITEDATA, get_cbc);
	curl_easy_setopt (get_c, CURLOPT_FAILONERROR, 1);
	curl_easy_setopt (get_c, CURLOPT_TIMEOUT, 150L);
	curl_easy_setopt (get_c, CURLOPT_CONNECTTIMEOUT, 15L);
	curl_easy_setopt (get_c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
	curl_easy_setopt (get_c, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt (get_c, CURLOPT_SHARE, share_c);

	curl_easy_setopt (put_c, CURLOPT_WRITEFUNCTION, &copyBuffer);
	curl_easy_setopt (put_c, CURLOPT_WRITEDATA, get_cbc);
	curl_easy_setopt (put_c, CURLOPT_READFUNCTION, &putBuffer);
	curl_easy_setopt (put_c, CURLOPT_READDATA, put_cbc);
	curl_easy_setopt (put_c, CURLOPT_UPLOAD, 1L);
	curl_easy_setopt (put_c, CURLOPT_PUT, 1L);
	curl_easy_setopt (put_c, CURLOPT_FAILONERROR, 1);
	curl_easy_setopt (put_c, CURLOPT_TIMEOUT, 150L);
	curl_easy_setopt (put_c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
	curl_easy_setopt (put_c, CURLOPT_CONNECTTIMEOUT, 15L);
	curl_easy_setopt (put_c, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt (put_c, CURLOPT_SHARE, share_c);

	curl_easy_setopt (get_events_c, CURLOPT_WRITEFUNCTION, &copyBufferEvents);
	curl_easy_setopt (get_events_c, CURLOPT_WRITEDATA, get_events_cbc);
	curl_easy_setopt (get_events_c, CURLOPT_FAILONERROR, 1);
	curl_easy_setopt (get_events_c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
	curl_easy_setopt (get_events_c, CURLOPT_SHARE, share_c);

#if SSL == 1
	curl_easy_setopt (get_c, CURLOPT_SSL_VERIFYPEER, 0 );
	curl_easy_setopt (get_c, CURLOPT_SSLCERT, "/home/tibo/client-cert.pem" );
	curl_easy_setopt (get_c, CURLOPT_KEYPASSWD, "thiboth" );
	curl_easy_setopt (put_c, CURLOPT_SSL_VERIFYPEER, 0 );
	curl_easy_setopt (put_c, CURLOPT_SSLCERT, "/home/tibo/client-cert.pem" );
	curl_easy_setopt (put_c, CURLOPT_KEYPASSWD, "thiboth" );
	curl_easy_setopt (get_events_c, CURLOPT_SSL_VERIFYPEER, 0 );
	curl_easy_setopt (get_events_c, CURLOPT_SSLCERT, "/home/tibo/client-cert.pem" );
	curl_easy_setopt (get_events_c, CURLOPT_KEYPASSWD, "thiboth" );
#endif

	return 0;
}

int deinit_curl()
{
	curl_easy_cleanup(get_c);
	curl_easy_cleanup(put_c);
	curl_easy_cleanup(get_events_c);
	curl_share_cleanup(share_c);

	free(get_buf);
	free(get_cbc);
	free(put_cbc);

	free(get_events_buf);
	free(get_events_cbc);

	return 0;
}

char * http_get_function(char *_url)
{
	char *_get_data=NULL;
	CURLcode errornum;

	curl_easy_setopt (get_c, CURLOPT_URL, _url);

	get_cbc->pos = 0;

	if (CURLE_OK != (errornum = curl_easy_perform (get_c)))
	{
		fprintf (stderr,
		         "GET curl_easy_perform failed: `%s'\n",
		         curl_easy_strerror (errornum));
		long http_code;
		curl_easy_getinfo(get_c, CURLINFO_RESPONSE_CODE, &http_code);
		printf("HTTP error : %d\n", http_code);
		return NULL;
	}	

	_get_data = (char*)malloc( ( get_cbc->pos +1 ) * sizeof(char));

	strncpy(_get_data, get_cbc->buf, get_cbc->pos);
	_get_data[get_cbc->pos] = '\0';

	return _get_data;
}

char * http_put_function(char *_url, char *_data)
{
	char *_received_data=NULL;
	CURLcode errornum;

	put_cbc->buf = _data;
    put_cbc->size = strlen(_data);
    put_cbc->pos = 0;

	if(put_c) 
	{
		curl_easy_setopt (put_c, CURLOPT_URL, _url);

		if (CURLE_OK != (errornum = curl_easy_perform (put_c)))
		{
			fprintf (stderr,
			         "PUT curl_easy_perform failed: `%s'\n",
			         curl_easy_strerror (errornum));
			long http_code;
			curl_easy_getinfo(get_c, CURLINFO_RESPONSE_CODE, &http_code);
			printf("HTTP error : %d\n", http_code);
			return NULL;
		};

		_received_data = (char*)malloc((put_cbc->pos + 1) * sizeof(char));

		strncpy(_received_data, put_cbc->buf, put_cbc->pos);

		return _received_data;  
	}

	return NULL;
}


char * http_get_events_function(void *_url)
{
	char *_get_data=NULL;
	CURLcode errornum;

	curl_easy_setopt (get_events_c, CURLOPT_URL,(char*) _url);

	printf("START of events\n");

	if (CURLE_OK != (errornum = curl_easy_perform (get_events_c)))
	{
		fprintf (stderr,
		         "GET events curl_easy_perform failed: `%s'\n",
		         curl_easy_strerror (errornum));
		long http_code;
		curl_easy_getinfo(get_c, CURLINFO_RESPONSE_CODE, &http_code);
		printf("HTTP error : %d\n", http_code);
		return NULL;
	}

	printf("END of events\n");

	/*_get_data = (char*)malloc( ( get_events_cbc->pos +1 ) * sizeof(char));

	strncpy(_get_data, get_events_cbc->buf, get_events_cbc->pos);
	_get_data[get_events_cbc->pos] = '\0';*/

	return NULL;
}