#include <stdexcept>

#include <iostream>
#include "easy_curl.h"

#define DEBUG_CURL

#ifdef DEBUG_CURL

#include <sstream>

namespace
{
	std::string get_name(CURL* handle)
	{
		char buf[30];
		sprintf(buf, "%u.log", handle);
		return buf;
	}

	void call_debug(CURL* handle, const std::string& text)
	{
		std::ofstream out(get_name(handle).c_str(), std::ios::app);
		out << std::endl << "-------- [CALL] " << text << " --------" << std::endl << std::endl;
		out.close();
	}

	int curl_debug(CURL* handle, curl_infotype type, char* str, size_t len, void *)
	{
		std::ofstream out(get_name(handle).c_str(), std::ios::app);

		switch (type)
		{
			case CURLINFO_TEXT:
			{
				out << "[info] ";
				break;
			}

			case CURLINFO_HEADER_OUT:
			{
				out << ">> send: ";
				break;
			}

			case CURLINFO_DATA_OUT:
			{
				out << ">> send data (skip log)" << std::endl;
				return 0;
				break;
			}

			case CURLINFO_HEADER_IN:
			{
				out << "<< recv: ";
				break;
			}

			case CURLINFO_DATA_IN:
			{
				out << "<< recv data (skip log)" << std::endl;
				return 0;
				break;
			}
		}

		out << std::string(str, len);
		out.close();
		
		return 0;
	}
}
#endif

easy_curl::easy_curl() : m_curl(curl_easy_init())
{
	reset();
}

easy_curl::~easy_curl()
{
	curl_easy_cleanup(m_curl);
}

CURLcode easy_curl::upload(const std::string& from, const std::string& to)
{
	#ifdef DEBUG_CURL
		call_debug(m_curl, "upload(" + from + ", " + to + ")");
	#endif

	reset();

	if(!m_curl) 
		return m_error = CURLE_FAILED_INIT;

	m_infile.clear();
	m_infile.open(from.c_str(), std::ios::binary);
	if(!m_infile.is_open())
		throw std::runtime_error("Cannot open \"" + to + "\" for reading.");

	curl_easy_setopt(m_curl, CURLOPT_URL, to.c_str());
	curl_easy_setopt(m_curl, CURLOPT_HEADER, 0);
	curl_easy_setopt(m_curl, CURLOPT_UPLOAD, 1);
	curl_easy_setopt(m_curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);
	curl_easy_setopt(m_curl, CURLOPT_READDATA, this);
	curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, easy_curl::data_reader);

	m_error = curl_easy_perform(m_curl);
	m_infile.close();

	return m_error;
}

CURLcode easy_curl::ftp_cmd(const std::string& url, const std::string& cmd)
{
	#ifdef DEBUG_CURL
		call_debug(m_curl, "ftp_cmd(" + url + ", " + cmd + ")");
	#endif

	reset();

	if(!m_curl) 
		return m_error = CURLE_FAILED_INIT;

	curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1);
	curl_easy_setopt(m_curl, CURLOPT_HEADER, 0);
	curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
	
	curl_slist* list = NULL;
	list = curl_slist_append(list, cmd.c_str());
	curl_easy_setopt(m_curl, CURLOPT_POSTQUOTE, list);
	
	m_error = curl_easy_perform(m_curl);

	curl_slist_free_all(list);
  
	return m_error;
}

void easy_curl::reset()
{
	m_error = CURLE_OK;

	curl_easy_reset(m_curl);
	curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 1);
	//curl_easy_setopt(m_curl, CURLOPT_MAXREDIRS, -1);

	#ifdef DEBUG_CURL
		curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(m_curl, CURLOPT_DEBUGFUNCTION, curl_debug);
	#endif
}


CURLcode easy_curl::get_err_no() const
{
	return m_error;
}

std::string easy_curl::get_err_str() const
{
	return curl_easy_strerror(m_error);
}

size_t easy_curl::data_reader(void* buffer, size_t size, size_t bytes, void* object)
{
	easy_curl* obj = reinterpret_cast<easy_curl*>(object);
	size_t count = size * bytes;
	obj->m_infile.read(reinterpret_cast<char*>(buffer), count);
	return obj->m_infile.gcount();
}
