/** 
 * @file lluseroperation.cpp
 * @brief LLUserOperation class definition.
 *
 * $LicenseInfo:firstyear=2002&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$
 */

#include "linden_common.h"

#include "lluseroperation.h"

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

LLUserOperationMgr* gUserOperationMgr = NULL;

///----------------------------------------------------------------------------
/// Class LLUserOperation
///----------------------------------------------------------------------------

LLUserOperation::LLUserOperation(const LLUUID& agent_id)
:	mAgentID(agent_id),
	mTimer(),
	mNoExpire(FALSE)
{
	mTransactionID.generate();
}

LLUserOperation::LLUserOperation(const LLUUID& agent_id,
								 const LLUUID& transaction_id) :
	mAgentID(agent_id),
	mTransactionID(transaction_id),
	mTimer(),
	mNoExpire(FALSE)
{
}

// protected constructor which is used by base classes that determine
// transaction, agent, et. after construction.
LLUserOperation::LLUserOperation() :
	mTimer(),
	mNoExpire(FALSE)
{
}

LLUserOperation::~LLUserOperation()
{
}

void LLUserOperation::SetNoExpireFlag(const BOOL flag)
{
	mNoExpire = flag;
}

BOOL LLUserOperation::isExpired()
{
	if (!mNoExpire)
	{
		const F32 EXPIRE_TIME_SECS = 10.f;
		return mTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS;
	}
	return FALSE;
}

void LLUserOperation::expire()
{
	// by default, do do anything.
}

///----------------------------------------------------------------------------
/// Class LLUserOperationMgr
///----------------------------------------------------------------------------

LLUserOperationMgr::LLUserOperationMgr()
{
}


LLUserOperationMgr::~LLUserOperationMgr()
{
	if (mUserOperationList.size() > 0)
	{
		llwarns << "Exiting with user operations pending." << llendl;
	}
}


void LLUserOperationMgr::addOperation(LLUserOperation* op)
{
	if(!op)
	{
		llwarns << "Tried to add null op" << llendl;
		return;
	}
	LLUUID id = op->getTransactionID();
	llassert(mUserOperationList.count(id) == 0);
	mUserOperationList[id] = op;
}


LLUserOperation* LLUserOperationMgr::findOperation(const LLUUID& tid)
{
	user_operation_list_t::iterator iter = mUserOperationList.find(tid);
	if (iter != mUserOperationList.end())
		return iter->second;
	else
		return NULL;
}


BOOL LLUserOperationMgr::deleteOperation(LLUserOperation* op)
{
	size_t rv = 0;
	if(op)
	{
		LLUUID id = op->getTransactionID();
		rv = mUserOperationList.erase(id);
		delete op;
		op = NULL;
	}
	return rv ? TRUE : FALSE;
}

void LLUserOperationMgr::deleteExpiredOperations()
{
	const S32 MAX_OPS_CONSIDERED = 2000;
	S32 ops_left = MAX_OPS_CONSIDERED;
	LLUserOperation* op = NULL;
	user_operation_list_t::iterator it;
	if(mLastOperationConsidered.isNull())
	{
		it = mUserOperationList.begin();
	}
	else
	{
		it = mUserOperationList.lower_bound(mLastOperationConsidered);
	}
	while((ops_left--) && (it != mUserOperationList.end()))
	{
		op = (*it).second;
		if(op && op->isExpired())
		{
			lldebugs << "expiring: " << (*it).first << llendl;
			op->expire();
			mUserOperationList.erase(it++);
			delete op;
		}
		else if(op)
		{
			++it;
		}
		else
		{
			mUserOperationList.erase(it++);
		}
	}
	if(it != mUserOperationList.end())
	{
		mLastOperationConsidered = (*it).first;
	}
	else
	{
		mLastOperationConsidered.setNull();
	}
}


///----------------------------------------------------------------------------
/// Local function definitions
///----------------------------------------------------------------------------