/** 
 * @file lliopipe.h
 * @author Phoenix
 * @date 2004-11-18
 * @brief Declaration of base IO class
 *
 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, 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 LL_LLIOPIPE_H
#define LL_LLIOPIPE_H

#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "llwin32headerslean.h"
#include "apr_poll.h"

#include "llsd.h"

class LLIOPipe;
class LLPumpIO;
class LLBufferArray;
class LLChannelDescriptors;

// Debugging schmutz for deadlocks
//#define LL_DEBUG_PUMPS
#ifdef LL_DEBUG_PUMPS
void pump_debug(const char *file, S32 line);
#define PUMP_DEBUG pump_debug(__FILE__, __LINE__);
#define END_PUMP_DEBUG pump_debug("none", 0);
#else /* LL_DEBUG_PUMPS */
#define PUMP_DEBUG
#define END_PUMP_DEBUG
#endif


/**
 * intrusive pointer support
 */
void intrusive_ptr_add_ref(LLIOPipe* p);
void intrusive_ptr_release(LLIOPipe* p);

/** 
 * @class LLIOPipe
 * @brief This class is an abstract base class for data processing units
 * @see LLPumpIO
 *
 * The LLIOPipe is a base class for implementing the basic non-blocking
 * processing of data subsystem in our system.
 *
 * Implementations of this class should behave like a stateful or
 * stateless signal processor. Each call to <code>process()</code>
 * hands the pipe implementation a buffer and a set of channels in the
 * buffer to process, and the pipe returns the status of the
 * operation. This is an abstract base class and developer created
 * concrete implementations provide block or stream based processing
 * of data to implement a particular protocol.
 */
class LLIOPipe
{
public:
	/** 
	 * @brief I have decided that IO objects should have a reference
	 * count. In general, you can pass bald LLIOPipe pointers around
	 * as you need, but if you need to maintain a reference to one,
	 * you need to hold a ptr_t.
	 */
	typedef boost::intrusive_ptr<LLIOPipe> ptr_t;

	/** 
	 * @brief Scattered memory container.
	 */
	typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;

	/** 
	 * @brief Enumeration for IO return codes
	 *
	 * A status code a positive integer value is considered a success,
	 * but may indicate special handling for future calls, for
	 * example, issuing a STATUS_STOP to an LLIOSocketReader instance
	 * will tell the instance to stop reading the socket. A status
	 * code with a negative value means that a problem has been
	 * encountered which will require further action on the caller or
	 * a developer to correct. Some mechanisms, such as the LLPumpIO
	 * may depend on this definition of success and failure.
	 */
	enum EStatus
	{
		// Processing occurred normally, future calls will be accepted.
		STATUS_OK = 0,

		// Processing occured normally, but stop unsolicited calls to
		// process.
		STATUS_STOP = 1,

		// This pipe is done with the processing. Future calls to
		// process will be accepted as long as new data is available.
		STATUS_DONE = 2,

		// This pipe is requesting that it become the head in a process.
		STATUS_BREAK = 3,

		// This pipe is requesting that it become the head in a process.
		STATUS_NEED_PROCESS = 4,

		// Keep track of the highest number of success codes here.
		STATUS_SUCCESS_COUNT = 5,

		// A generic error code.
		STATUS_ERROR = -1,

		// This method has not yet been implemented. This usually
		// indicates the programmer working on the pipe is not yet
		// done.
		STATUS_NOT_IMPLEMENTED = -2,

		// This indicates that a pipe precondition was not met. For
		// example, many pipes require an element to appear after them
		// in a chain (ie, mNext is not null) and will return this in
		// response to method calls. To recover from this, it will
		// require the caller to adjust the pipe state or may require
		// a dev to adjust the code to satisfy the preconditions.
		STATUS_PRECONDITION_NOT_MET = -3,

		// This means we could not connect to a remote host.
		STATUS_NO_CONNECTION = -4,

		// The connection was lost.
		STATUS_LOST_CONNECTION = -5,

		// The totoal process time has exceeded the timeout.
		STATUS_EXPIRED = -6,

		// Keep track of the count of codes here.
		STATUS_ERROR_COUNT = 6,
	};

	/** 
	 * @brief Helper function to check status.
	 *
	 * When writing code to check status codes, if you do not
	 * specifically check a particular value, use this method for
	 * checking an error condition.
	 * @param status The status to check.
	 * @return Returns true if the code indicates an error occurred.
	 */
	inline static bool isError(EStatus status)
	{
		return ((S32)status < 0);
	}

	/** 
	 * @brief Helper function to check status.
	 *
	 * When writing code to check status codes, if you do not
	 * specifically check a particular value, use this method for
	 * checking an error condition.
	 * @param status The status to check.
	 * @return Returns true if the code indicates no error was generated.
	 */
	inline static bool isSuccess(EStatus status)
	{
		return ((S32)status >= 0);
	}

	/** 
	 * @brief Helper function to turn status into a string.
	 *
	 * @param status The status to check.
	 * @return Returns the name of the status code or empty string on failure.
	 */
	static std::string lookupStatusString(EStatus status);

	/** 
	 * @brief Process the data in buffer.
	 *
	 * @param data The data processed
	 * @param eos True if this function call is the last because end of stream.
	 * @param pump The pump which is calling process. May be NULL.
	 * @param context Shared meta-data for the process.
	 * @return Returns a status code from the operation.
	 */
	EStatus process(
		const LLChannelDescriptors& channels,
		buffer_ptr_t& buffer,
		bool& eos,
		LLSD& context,
		LLPumpIO* pump);

	/** 
	 * @brief Give this pipe a chance to handle a generated error
	 *
	 * If this pipe is in a chain being processed by a pump, and one
	 * of the pipes generates an error, the pump will rewind through
	 * the chain to see if any of the links can handle the error. For
	 * example, if a connection is refused in a socket connection, the
	 * socket client can try to find a new destination host. Return an
	 * error code if this pipe does not handle the error passed in.
	 * @param status The status code for the error
	 * @param pump The pump which was calling process before the error
	 * was generated.
	 * @return Returns a status code from the operation. Returns an
	 * error code if the error passed in was not handled. Returns
	 * STATUS_OK to indicate the error has been handled.
	 */
	virtual EStatus handleError(EStatus status, LLPumpIO* pump);

	/**
	 * @brief Base Destructor - do not call <code>delete</code> directly.
	 */
	virtual ~LLIOPipe();

	virtual bool isValid() ;

protected:
	/**
	 * @brief Base Constructor.
	 */
	LLIOPipe();

	/** 
	 * @brief Process the data in buffer
	 */
	virtual EStatus process_impl(
		const LLChannelDescriptors& channels,
		buffer_ptr_t& buffer,
		bool& eos,
		LLSD& context,
		LLPumpIO* pump) = 0;

private:
	friend void intrusive_ptr_add_ref(LLIOPipe* p);
	friend void intrusive_ptr_release(LLIOPipe* p);
	U32 mReferenceCount;
};

inline void intrusive_ptr_add_ref(LLIOPipe* p)
{
	++p->mReferenceCount;
}
inline void intrusive_ptr_release(LLIOPipe* p)
{
	if(p && 0 == --p->mReferenceCount)
	{
		delete p;
	}
}

#endif // LL_LLIOPIPE_H
