#include <iostream>

#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include "FtpParse.h"
#include "FtpHelper.h"

FtpHelper::FtpHelper(const std::string &url, bool verbose) : m_curl(0), m_url(url), m_verbose(verbose)
{
	curl_global_init(CURL_GLOBAL_DEFAULT);
	m_curl = curl_easy_init();
}

FtpHelper::~FtpHelper()
{
	curl_easy_cleanup(m_curl);
	curl_global_cleanup();
}

FtpHelper::StatusCodeHelper FtpHelper::GetFileList(FileList &fileList, bool includeDirectories)
{
	FileListCallBackData fileListCallBackData;

	curl_easy_reset(m_curl);
	curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
	curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 1);
	curl_easy_setopt(m_curl, CURLOPT_TRANSFERTEXT, 1);
	curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, CallbackFunction);
	curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &fileListCallBackData);

	if (m_verbose)
		curl_easy_setopt(m_curl, CURLOPT_VERBOSE, TRUE);

	CURLcode res = curl_easy_perform(m_curl);
	if (CURLE_OK != res)
		throw FtpException("unable to get file list, curl error ", res);

	return fileListCallBackData.GetFileList(fileList, includeDirectories);
}

FtpHelper::StatusCodeHelper FtpHelper::GetFile(const std::string &filename)
{
	FileCallBackData fileCallBackData(filename);
	
	std::string url = m_url + "/" + filename;
	curl_easy_reset(m_curl);
	curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
	curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 1);
	curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, CallbackFunction);
	curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &fileCallBackData);
	if (m_verbose)
		curl_easy_setopt(m_curl, CURLOPT_VERBOSE, TRUE);
	CURLcode res = curl_easy_perform(m_curl);
	if (CURLE_OK != res)
		return StatusCodeHelper(STATUS_CODE_ERROR, curl_easy_strerror(res));

	return StatusCodeHelper();
}

FtpHelper::StatusCodeHelper FtpHelper::DeleteFile(const std::string &filename)
{
	FileDeleteCallBackData fileDeleteCallBack;
	struct curl_slist *list = NULL;
	std::string ftpCommand = "DELE " + filename;
	list = curl_slist_append(list, ftpCommand.c_str());
	curl_easy_reset(m_curl);
	curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
	curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 1);
	curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, CallbackFunction);
	curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &fileDeleteCallBack);
	curl_easy_setopt(m_curl, CURLOPT_QUOTE, list);
	if (m_verbose)
		curl_easy_setopt(m_curl, CURLOPT_VERBOSE, TRUE);
	CURLcode res = curl_easy_perform(m_curl);
	if (CURLE_OK != res)
		return StatusCodeHelper(STATUS_CODE_ERROR, curl_easy_strerror(res));

	return StatusCodeHelper();
}

size_t FtpHelper::CallbackFunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
	size_t realsize = size * nmemb;
	CallBackData *pCallBackData = static_cast<CallBackData *>(stream);

	return pCallBackData->AppendData(ptr, realsize);
}

FtpHelper::StatusCodeHelper FtpHelper::FileListCallBackData::GetFileList(FileList &fileList, bool includeDirectories)
{
	size_t start = 0, end = 0;
	struct ftpparse parseInfo;

	std::string &data = m_data;
	while((end = data.find_first_of("\r\n", start)) != data.npos)
	{
		if (ftpparse(&parseInfo, const_cast<char*>(data.c_str()) + start, static_cast<int>(end-start)))
		{
			if (!(includeDirectories || !parseInfo.flagtrycwd))
			{
				start = end+2;
				continue;
			}

			std::string fileName = std::string(parseInfo.name, parseInfo.namelen);
			fileList.push_back(fileName);
		}
		else
			return  StatusCodeHelper(STATUS_CODE_ERROR, "unable to parse file list");
		
		start = end+2;
	}

	return StatusCodeHelper();
}

size_t FtpHelper::FileListCallBackData::AppendData(void *ptr, size_t size)
{
	m_data.append(static_cast<char *>(ptr), size);
	
	return size;
}

FtpHelper::FileCallBackData::FileCallBackData(const std::string &filename, int fileSize) 
{
	m_file.open(filename.c_str());
	if (!m_file)
		throw FtpException("Unable to create file " + filename);

}

size_t FtpHelper::FileCallBackData::AppendData(void *ptr, size_t size)
{
	m_file.write(static_cast<char*>(ptr), static_cast<std::streamsize>(size));
	return size;
}
		