//------------------------------------------------------------------------------
// <copyright file="CurlUtils.h" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//   CurlUtils wraps the curl library and provides implementations needed by SV3
//   remoting.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef __SV3REMOTINGLIB_CURLUTILS_H__
#define __SV3REMOTINGLIB_CURLUTILS_H__

#include <ostream>
#include <vector>
#include <curl/curl.h>
#include "LqrSMACSharedLib.h"
#include "LqrSMACTempFile.h"
#include "LqrSMACDeclareException.h"

namespace LiquidRobotics {
namespace SMACFramework {
	class Logger;
}
namespace SV3RemotingLib {
namespace CurlUtils {

class SHARED_LIB_PUBLIC CurlEasyException: public std::runtime_error
{
public:
	CurlEasyException(CURLcode _code, const std::string& whatArg) throw();

	const CURLcode code;
};

class SHARED_LIB_PUBLIC CurlFormException: public std::runtime_error
{
public:
	CurlFormException(CURLFORMcode _code, const std::string& whatArg) throw();

	const CURLFORMcode code;
};

class SHARED_LIB_PUBLIC CurlMultiException: public std::runtime_error
{
public:
	CurlMultiException(CURLMcode _code, const std::string& whatArg) throw();

	const CURLMcode code;
};

class SHARED_LIB_PUBLIC CurlConnectionFailure: public std::runtime_error
{
public:
	CurlConnectionFailure(const std::string& whatArg) throw();
};

DECLARE_EXCEPTION(ConnectionFailure,std::runtime_error);
DECLARE_EXCEPTION(ServerDidNotRespond,std::runtime_error);

struct SHARED_LIB_PUBLIC FormAddFileCopyFromString
{
	FormAddFileCopyFromString(curl_httppost*& formpost, curl_httppost*& lastptr, const std::string& fileName,
			const std::string& content, bool contentIsFileName = false);

	LiquidRobotics::SMACFramework::TempFile tf;
};

SHARED_LIB_PUBLIC void ThrowEasy(CURLcode code, const std::string& func);
SHARED_LIB_PUBLIC void ThrowFormAddError(CURLFORMcode res);
SHARED_LIB_PUBLIC void ThrowMulti(CURLMcode code, const std::string& func);

struct SHARED_LIB_PUBLIC CurlOperationBase
{
	CurlOperationBase(const std::string& _url);
	virtual ~CurlOperationBase();

	void InitCurl(bool multiInit = false);

	typedef std::vector<std::string> StringArray;
	StringArray headers;
	std::string body;

protected:

	template<typename T>
	void SetOpt(CURLoption id, T t)
	{
		ThrowEasy(curl_easy_setopt(curl, id, t), "curl_easy_setopt");
	}

	void cleanup();

	static size_t WriteDataFunc(char* ptr, size_t size, size_t nmemb, void* userdata);
	size_t WriteData(char* ptr, size_t size, size_t nmemb);

	static size_t WriteHeaderFunc(void *ptr, size_t size, size_t nmemb, void *userdata);
	size_t WriteHeader(void *ptr, size_t size, size_t nmemb);

	CURL* curl;
	CURLM* multi_handle;
	const std::string url;

private:

	CurlOperationBase* pThis;
};

struct SHARED_LIB_PUBLIC PostFile : public CurlOperationBase
{
	PostFile(const std::string& url, const std::string& fileName, const std::string& fileContent,
			bool contentIsFileName = false, bool verbose = false, long curlTimeout = 300);
	virtual ~PostFile();

private:

	struct MyFdSet : fd_set
	{
		MyFdSet();

		bool HasBits() const;
	};

	struct MyTimeval : timeval
	{
		MyTimeval();
		void Reset(long timeout_value);
	};

	curl_httppost* formpost;
	curl_httppost* lastptr;
	curl_slist* headerlist;
};

struct SHARED_LIB_PUBLIC PostFile2 : public CurlOperationBase
{
	PostFile2(const std::string& url, const std::string& fileName, const std::string& fileContent,
			LiquidRobotics::SMACFramework::Logger& logger,
			bool contentIsFileName = false, bool verbose = false, long curlTimeout = 300);
	virtual ~PostFile2();

private:

	curl_httppost* formpost;
	curl_httppost* lastptr;
	curl_slist* headerlist;
};

struct SHARED_LIB_PUBLIC HttpGet : public CurlOperationBase
{
	HttpGet(const std::string& _url, LiquidRobotics::SMACFramework::Logger& logger, bool _verbose = false, long _curlTimeout = 300);
	virtual ~HttpGet();

	void DebugOut(std::ostream& stm) const;
	void Close();

private:

	const bool verbose;
	const long curlTimeout;
};

}
}
}

#endif
