/** 
 * @file lltransfermanager.h
 * @brief Improved transfer mechanism for moving data through the
 * message system.
 *
 * $LicenseInfo:firstyear=2006&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_LLTRANSFERMANAGER_H
#define LL_LLTRANSFERMANAGER_H

#include <map>
#include <list>

#include "llhost.h"
#include "lluuid.h"
#include "llthrottle.h"
#include "llpriqueuemap.h"
#include "llassettype.h"

//
// Definition of the manager class for the new LLXfer replacement.
// Provides prioritized, bandwidth-throttled transport of arbitrary
// binary data between host/circuit combos
//


typedef enum e_transfer_channel_type
{
	LLTCT_UNKNOWN = 0,
	LLTCT_MISC,
	LLTCT_ASSET,
	LLTCT_NUM_TYPES
} LLTransferChannelType;


typedef enum e_transfer_source_type
{
	LLTST_UNKNOWN = 0,
	LLTST_FILE,
	LLTST_ASSET,
	LLTST_SIM_INV_ITEM,	// Simulator specific, may not be handled
	LLTST_SIM_ESTATE,	// Simulator specific, may not be handled
	LLTST_NUM_TYPES
} LLTransferSourceType;


typedef enum e_transfer_target_type
{
	LLTTT_UNKNOWN = 0,
	LLTTT_FILE,
	LLTTT_VFILE,
	LLTTT_NUM_TYPES
} LLTransferTargetType;


// Errors are negative, expected values are positive.
typedef enum e_status_codes
{
	LLTS_OK = 0,
	LLTS_DONE = 1,
	LLTS_SKIP = 2,
	LLTS_ABORT = 3,
	LLTS_ERROR = -1,
	LLTS_UNKNOWN_SOURCE = -2, // Equivalent of a 404
	LLTS_INSUFFICIENT_PERMISSIONS = -3	// Not enough permissions
} LLTSCode;

// Types of requests for estate wide information
typedef enum e_estate_type
{
	ET_Covenant = 0,
	ET_NONE = -1
} EstateAssetType;

class LLMessageSystem;
class LLDataPacker;

class LLTransferConnection;
class LLTransferSourceChannel;
class LLTransferTargetChannel;
class LLTransferSourceParams;
class LLTransferTargetParams;
class LLTransferSource;
class LLTransferTarget;

class LLTransferManager
{
public:
	LLTransferManager();
	virtual ~LLTransferManager();

	void init();
	void cleanup();

	void updateTransfers();	// Called per frame to push packets out on the various different channels.
	void cleanupConnection(const LLHost &host);


	LLTransferSourceChannel *getSourceChannel(const LLHost &host, const LLTransferChannelType stype);
	LLTransferTargetChannel *getTargetChannel(const LLHost &host, const LLTransferChannelType stype);

	LLTransferSource *findTransferSource(const LLUUID &transfer_id);

	BOOL						isValid() const			{ return mValid; }

	static void processTransferRequest(LLMessageSystem *mesgsys, void **);
	static void processTransferInfo(LLMessageSystem *mesgsys, void **);
	static void processTransferPacket(LLMessageSystem *mesgsys, void **);
	static void processTransferAbort(LLMessageSystem *mesgsys, void **);

	static void reliablePacketCallback(void **, S32 result);

	S32	getTransferBitsIn(const LLTransferChannelType tctype) const		{ return mTransferBitsIn[tctype]; }
	S32 getTransferBitsOut(const LLTransferChannelType tctype) const	{ return mTransferBitsOut[tctype]; }
	void resetTransferBitsIn(const LLTransferChannelType tctype)		{ mTransferBitsIn[tctype] = 0; }
	void resetTransferBitsOut(const LLTransferChannelType tctype)		{ mTransferBitsOut[tctype] = 0; }
	void addTransferBitsIn(const LLTransferChannelType tctype, const S32 bits)	{ mTransferBitsIn[tctype] += bits; }
	void addTransferBitsOut(const LLTransferChannelType tctype, const S32 bits)	{ mTransferBitsOut[tctype] += bits; }
protected:
	LLTransferConnection		*getTransferConnection(const LLHost &host);
	BOOL						removeTransferConnection(const LLHost &host);

protected:
	// Convenient typedefs
	typedef std::map<LLHost, LLTransferConnection *> host_tc_map;

	BOOL	mValid;
	LLHost	mHost;

	S32		mTransferBitsIn[LLTTT_NUM_TYPES];
	S32		mTransferBitsOut[LLTTT_NUM_TYPES];

	// We keep a map between each host and LLTransferConnection.
	host_tc_map mTransferConnections;
};


//
// Keeps tracks of all channels to/from a particular host.
//
class LLTransferConnection
{
public:
	LLTransferConnection(const LLHost &host);
	virtual ~LLTransferConnection();

	void updateTransfers();

	LLTransferSourceChannel *getSourceChannel(const LLTransferChannelType type);
	LLTransferTargetChannel *getTargetChannel(const LLTransferChannelType type);

	// Convenient typedefs
	typedef std::list<LLTransferSourceChannel *>::iterator tsc_iter;
	typedef std::list<LLTransferTargetChannel *>::iterator ttc_iter;
	friend class LLTransferManager;
protected:

	LLHost									mHost;
	std::list<LLTransferSourceChannel *>	mTransferSourceChannels;
	std::list<LLTransferTargetChannel *>	mTransferTargetChannels;

};


//
// A channel which is pushing data out.
//

class LLTransferSourceChannel
{
public:
	LLTransferSourceChannel(const LLTransferChannelType channel_type,
							const LLHost &host);
	virtual ~LLTransferSourceChannel();

	void updateTransfers();

	void updatePriority(LLTransferSource *tsp, const F32 priority);

	void				addTransferSource(LLTransferSource *sourcep);
	LLTransferSource	*findTransferSource(const LLUUID &transfer_id);
	void				deleteTransfer(LLTransferSource *tsp);

	void					setThrottleID(const S32 throttle_id)	{ mThrottleID = throttle_id; }

	LLTransferChannelType	getChannelType() const		{ return mChannelType; }
	LLHost					getHost() const				{ return mHost; }

protected:
	typedef std::list<LLTransferSource *>::iterator ts_iter;

	LLTransferChannelType				mChannelType;
	LLHost								mHost;
	LLPriQueueMap<LLTransferSource*>	mTransferSources;

	// The throttle that this source channel should use
	S32									mThrottleID;
};


//
// A channel receiving data from a source.
//
class LLTransferTargetChannel
{
public:
	LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host);
	virtual ~LLTransferTargetChannel();

	void requestTransfer(const LLTransferSourceParams &source_params,
						 const LLTransferTargetParams &target_params,
						 const F32 priority);

	LLTransferTarget		*findTransferTarget(const LLUUID &transfer_id);
	void					deleteTransfer(LLTransferTarget *ttp);


	LLTransferChannelType	getChannelType() const		{ return mChannelType; }
	LLHost					getHost() const				{ return mHost; }

protected:
	void sendTransferRequest(LLTransferTarget *targetp,
							 const LLTransferSourceParams &params,
							 const F32 priority);

	void					addTransferTarget(LLTransferTarget *targetp);

	friend class LLTransferTarget;
	friend class LLTransferManager;
protected:
	typedef std::list<LLTransferTarget *>::iterator tt_iter;

	LLTransferChannelType			mChannelType;
	LLHost							mHost;
	std::list<LLTransferTarget *>	mTransferTargets;
};


class LLTransferSourceParams
{
public:
	LLTransferSourceParams(const LLTransferSourceType type) : mType(type) { }
	virtual ~LLTransferSourceParams();

	virtual void packParams(LLDataPacker &dp) const	= 0;
	virtual BOOL unpackParams(LLDataPacker &dp) = 0;

	LLTransferSourceType getType() const			{ return mType; }
	
protected:
	LLTransferSourceType mType;
};


//
// LLTransferSource is an interface, all transfer sources should be derived from it.
//
typedef LLTransferSource *(*LLTransferSourceCreateFunc)(const LLUUID &id, const F32 priority);

class LLTransferSource
{
public:

	LLUUID getID()				{ return mID; }

	friend class LLTransferManager;
	friend class LLTransferSourceChannel;

protected:
	LLTransferSource(const LLTransferSourceType source_type,
					 const LLUUID &request_id,
					 const F32 priority);
	virtual ~LLTransferSource();

	void					sendTransferStatus(LLTSCode status);	// When you've figured out your transfer status, do this

	virtual void			initTransfer() = 0;
	virtual F32				updatePriority() = 0;
	virtual LLTSCode		dataCallback(const S32 packet_id,
										 const S32 max_bytes,
										 U8 **datap,
										 S32 &returned_bytes,
										 BOOL &delete_returned) = 0;

	// The completionCallback is GUARANTEED to be called before the destructor.
	virtual void			completionCallback(const LLTSCode status) = 0;

	virtual void packParams(LLDataPacker& dp) const = 0;
	virtual BOOL unpackParams(LLDataPacker& dp) = 0;

	virtual S32				getNextPacketID()						{ return mLastPacketID + 1; }
	virtual void			setLastPacketID(const S32 packet_id)	{ mLastPacketID = packet_id; }


	// For now, no self-induced priority changes
	F32						getPriority()							{ return mPriority; }
	void					setPriority(const F32 pri)				{ mPriority = pri; }

	virtual void			abortTransfer(); // DON'T USE THIS ONE, used internally by LLTransferManager

	static LLTransferSource *createSource(const LLTransferSourceType stype,
										  const LLUUID &request_id,
										  const F32 priority);
	static void registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc);

	static void sSetPriority(LLTransferSource *&tsp, const F32 priority);
	static F32	sGetPriority(LLTransferSource *&tsp);
protected:
	typedef std::map<LLTransferSourceType, LLTransferSourceCreateFunc> stype_scfunc_map;
	static stype_scfunc_map sSourceCreateMap;

	LLTransferSourceType mType;
	LLUUID mID;
	LLTransferSourceChannel *mChannelp;
	F32		mPriority;
	S32		mSize;
	S32		mLastPacketID;
};


class LLTransferTargetParams
{
public:
	LLTransferTargetParams(const LLTransferTargetType type) : mType(type) {}
	LLTransferTargetType getType() const		{ return mType; }
protected:
	LLTransferTargetType mType;
};


class LLTransferPacket
{
	// Used for storing a packet that's being delivered later because it's out of order.
	// ONLY should be accessed by the following two classes, for now.
	friend class LLTransferTarget;
	friend class LLTransferManager;

protected:

	LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size);
	virtual ~LLTransferPacket();

protected:
	S32			mPacketID;
	LLTSCode	mStatus;
	U8			*mDatap;
	S32			mSize;
};


class LLTransferTarget
{
public:
	LLTransferTarget(
		LLTransferTargetType target_type,
		const LLUUID& transfer_id,
		LLTransferSourceType source_type);
	virtual ~LLTransferTarget();

	// Accessors
	LLUUID					getID() const			{ return mID; }
	LLTransferTargetType	getType() const			{ return mType; }
	LLTransferTargetChannel *getChannel() const		{ return mChannelp; }
	LLTransferSourceType getSourceType() const { return mSourceType; }

	// Static functionality
	static LLTransferTarget* createTarget(
		LLTransferTargetType target_type,
		const LLUUID& request_id,
		LLTransferSourceType source_type);

	// friends
	friend class LLTransferManager;
	friend class LLTransferTargetChannel;

protected:
	// Implementation
	virtual bool unpackParams(LLDataPacker& dp) = 0;
	virtual void applyParams(const LLTransferTargetParams &params) = 0;
	virtual LLTSCode		dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size) = 0;

	// The completionCallback is GUARANTEED to be called before the destructor, so all handling
	// of errors/aborts should be done here.
	virtual void			completionCallback(const LLTSCode status) = 0;

	void					abortTransfer();

	virtual S32				getNextPacketID()						{ return mLastPacketID + 1; }
	virtual void			setLastPacketID(const S32 packet_id)	{ mLastPacketID = packet_id; }
	void					setSize(const S32 size)					{ mSize = size; }
	void					setGotInfo(const BOOL got_info)			{ mGotInfo = got_info; }
	BOOL					gotInfo() const							{ return mGotInfo; }

	bool addDelayedPacket(
		const S32 packet_id,
		const LLTSCode status,
		U8* datap,
		const S32 size);

protected:
	typedef std::map<S32, LLTransferPacket *> transfer_packet_map;
	typedef std::map<S32, LLTransferPacket *>::iterator tpm_iter;

	LLTransferTargetType	mType;
	LLTransferSourceType mSourceType;
	LLUUID					mID;
	LLTransferTargetChannel *mChannelp;
	BOOL					mGotInfo;
	S32						mSize;
	S32						mLastPacketID;

	transfer_packet_map		mDelayedPacketMap; // Packets that are waiting because of missing/out of order issues
};


// Hack, here so it's publicly available even though LLTransferSourceInvItem is only available on the simulator
class LLTransferSourceParamsInvItem: public LLTransferSourceParams
{
public:
	LLTransferSourceParamsInvItem();
	virtual ~LLTransferSourceParamsInvItem() {}
	/*virtual*/ void packParams(LLDataPacker &dp) const;
	/*virtual*/ BOOL unpackParams(LLDataPacker &dp);

	void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
	void setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id);
	void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);

	LLUUID getAgentID() const						{ return mAgentID; }
	LLUUID getSessionID() const						{ return mSessionID; }
	LLUUID getOwnerID() const						{ return mOwnerID; }
	LLUUID getTaskID() const						{ return mTaskID; }
	LLUUID getItemID() const						{ return mItemID; }
	LLUUID getAssetID() const						{ return mAssetID; }
	LLAssetType::EType getAssetType() const			{ return mAssetType; }

protected:
	LLUUID				mAgentID;
	LLUUID				mSessionID;
	LLUUID				mOwnerID;
	LLUUID				mTaskID;
	LLUUID				mItemID;
	LLUUID				mAssetID;
	LLAssetType::EType	mAssetType;
};


// Hack, here so it's publicly available even though LLTransferSourceEstate is only available on the simulator
class LLTransferSourceParamsEstate: public LLTransferSourceParams
{
public:
	LLTransferSourceParamsEstate();
	virtual ~LLTransferSourceParamsEstate() {}
	/*virtual*/ void packParams(LLDataPacker &dp) const;
	/*virtual*/ BOOL unpackParams(LLDataPacker &dp);

	void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
	void setEstateAssetType(const EstateAssetType etype);
	void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);

	LLUUID getAgentID() const						{ return mAgentID; }
	LLUUID getSessionID() const						{ return mSessionID; }
	EstateAssetType getEstateAssetType() const		{ return mEstateAssetType; }
	LLUUID getAssetID() const					{ return mAssetID; }
	LLAssetType::EType getAssetType() const		{ return mAssetType; }

protected:
	LLUUID				mAgentID;
	LLUUID				mSessionID;
	EstateAssetType		mEstateAssetType;
	// these are set on the sim based on estateinfotype
	LLUUID				mAssetID;
	LLAssetType::EType	mAssetType;
};


extern LLTransferManager gTransferManager;

#endif//LL_LLTRANSFERMANAGER_H