/**
 * @file _httpservice.h
 * @brief Declarations for internal class providing HTTP service.
 *
 * $LicenseInfo:firstyear=2012&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012-2013, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef	_LLCORE_HTTP_SERVICE_H_
#define	_LLCORE_HTTP_SERVICE_H_


#include <vector>

#include "linden_common.h"
#include "llatomic.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "_httppolicyglobal.h"
#include "_httppolicyclass.h"


namespace LLCoreInt
{

class HttpThread;

}


namespace LLCore
{


class HttpRequestQueue;
class HttpPolicy;
class HttpLibcurl;
class HttpOpSetGet;


/// The HttpService class does the work behind the request queue.  It
/// oversees the HTTP workflow carrying out a number of tasks:
/// - Pulling requests from the global request queue
/// - Executing 'immediate' requests directly
/// - Prioritizing and re-queuing on internal queues the slower requests
/// - Providing cpu cycles to the libcurl plumbing
/// - Overseeing retry operations
///
/// Note that the service object doesn't have a pointer to any
/// reply queue.  These are kept by HttpRequest and HttpOperation
/// only.
///
/// Service, Policy and Transport
///
/// HttpService could have been a monolithic class combining a request
/// queue servicer, request policy manager and network transport.
/// Instead, to prevent monolithic growth and allow for easier
/// replacement, it was developed as three separate classes:  HttpService,
/// HttpPolicy and HttpLibcurl (transport).  These always exist in a
/// 1:1:1 relationship with HttpService managing instances of the other
/// two.  So, these classes do not use reference counting to refer
/// to one another, their lifecycles are always managed together.

class HttpService
{
protected:
	HttpService();
	virtual ~HttpService();

private:
	HttpService(const HttpService &);			// Not defined
	void operator=(const HttpService &);		// Not defined

public:
	enum EState
	{
		NOT_INITIALIZED = -1,
		INITIALIZED,			///< init() has been called
		RUNNING,				///< thread created and running
		STOPPED					///< thread has committed to exiting
	};

	// Ordered enumeration of idling strategies available to
	// threadRun's loop.  Ordered so that std::min on values
	// produces the most conservative result of multiple
	// requests.
	enum ELoopSpeed
	{
		NORMAL,					///< continuous polling of request, ready, active queues
		REQUEST_SLEEP			///< can sleep indefinitely waiting for request queue write
	};

	static void init(HttpRequestQueue *);
	static void term();

	/// Threading:  callable by any thread once inited.
	inline static HttpService * instanceOf()
		{
			return sInstance;
		}

	/// Return the state of the worker thread.  Note that the
	/// transition from RUNNING to STOPPED is performed by the
	/// worker thread itself.  This has two weaknesses:
	/// - race where the thread hasn't really stopped but will
	/// - data ordering between threads where a non-worker thread
	///   may see a stale RUNNING status.
	///
	/// This transition is generally of interest only to unit tests
	/// and these weaknesses shouldn't be any real burden.
	///
	/// Threading:  callable by any thread with above exceptions.
	static EState getState()
		{
			return sState;
		}

	/// Threading:  callable by any thread but uses @see getState() and
	/// acquires its weaknesses.
	static bool isStopped();

	/// Threading:  callable by init thread *once*.
	void startThread();

	/// Threading:  callable by worker thread.
	void stopRequested();

	/// Threading:  callable by worker thread.
	void shutdown();

	/// Try to find the given request handle on any of the request
	/// queues and reset the priority (and queue position) of the
	/// request if found.
	///
	/// @return			True if the request was found somewhere.
	///
	/// Threading:  callable by worker thread.
	bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
	
	/// Try to find the given request handle on any of the request
	/// queues and cancel the operation.
	///
	/// @return			True if the request was found and canceled.
	///
	/// Threading:  callable by worker thread.
	bool cancel(HttpHandle handle);
	
	/// Threading:  callable by worker thread.
	HttpPolicy & getPolicy()
		{
			return *mPolicy;
		}

	/// Threading:  callable by worker thread.
	HttpLibcurl & getTransport()
		{
			return *mTransport;
		}

	/// Threading:  callable by worker thread.
	HttpRequestQueue & getRequestQueue()
		{
			return *mRequestQueue;
		}

	/// Threading:  callable by consumer thread.
	HttpRequest::policy_t createPolicyClass();
	
protected:
	void threadRun(LLCoreInt::HttpThread * thread);
	
	ELoopSpeed processRequestQueue(ELoopSpeed loop);

protected:
	friend class HttpOpSetGet;
	friend class HttpRequest;
	
	// Used internally to describe what operations are allowed
	// on each policy option.
	struct OptionDescriptor
	{
		bool		mIsLong;
		bool		mIsDynamic;
		bool		mIsGlobal;
		bool		mIsClass;
		bool		mIsCallback;
	};
		
	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
							   long * ret_value);
	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
							   std::string * ret_value);
	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
								HttpRequest::policyCallback_t * ret_value);

	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
							   long value, long * ret_value);
	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
							   const std::string & value, std::string * ret_value);
	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
								HttpRequest::policyCallback_t value, 
								HttpRequest::policyCallback_t * ret_value);

protected:
	static const OptionDescriptor		sOptionDesc[HttpRequest::PO_LAST];
	static HttpService *				sInstance;
	
	// === shared data ===
	static volatile EState				sState;
	HttpRequestQueue *					mRequestQueue;	// Refcounted
	LLAtomicU32							mExitRequested;
	LLCoreInt::HttpThread *				mThread;
	
	// === working-thread-only data ===
	HttpPolicy *						mPolicy;		// Simple pointer, has ownership
	HttpLibcurl *						mTransport;		// Simple pointer, has ownership
	
	// === main-thread-only data ===
	HttpRequest::policy_t				mLastPolicy;
	
};  // end class HttpService

}  // end namespace LLCore

#endif // _LLCORE_HTTP_SERVICE_H_
