/** 
 * @file llviewerparcelmgr.cpp
 * @brief Viewer-side representation of owned land
 *
 * $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 "llviewerprecompiledheaders.h"

#include "llviewerparcelmgr.h"

// Library includes
#include "llaudioengine.h"
#include "indra_constants.h"
#include "llcachename.h"
#include "llgl.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llparcel.h"
#include "message.h"
#include "llfloaterreg.h"

// Viewer includes
#include "llagent.h"
#include "llagentaccess.h"
#include "llviewerparcelaskplay.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
//#include "llfirstuse.h"
#include "llfloaterbuyland.h"
#include "llfloatergroups.h"
#include "llpanelnearbymedia.h"
#include "llfloatersellland.h"
#include "llfloatertools.h"
#include "llparcelselection.h"
#include "llresmgr.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "llslurl.h"
#include "llstatusbar.h"
#include "llui.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
#include "llviewermenu.h"
#include "llviewerparcelmedia.h"
#include "llviewerparceloverlay.h"
#include "llviewerregion.h"
#include "llworld.h"
#include "roles_constants.h"
#include "llweb.h"
#include "llvieweraudio.h"
#include "llcorehttputil.h"

#include "llenvironment.h"

const F32 PARCEL_COLLISION_DRAW_SECS = 1.f;


// Globals

U8* LLViewerParcelMgr::sPackedOverlay = NULL;

LLUUID gCurrentMovieID = LLUUID::null;

LLPointer<LLViewerTexture> sBlockedImage;
LLPointer<LLViewerTexture> sPassImage;

// Local functions
void callback_start_music(S32 option, void* data);
void optionally_prepare_video(const LLParcel *parcelp);
void callback_prepare_video(S32 option, void* data);
void prepare_video(const LLParcel *parcelp);
void start_video(const LLParcel *parcelp);
void stop_video();
bool callback_god_force_owner(const LLSD&, const LLSD&);

struct LLGodForceOwnerData
{
	LLUUID mOwnerID;
	S32 mLocalID;
	LLHost mHost;

	LLGodForceOwnerData(
		const LLUUID& owner_id,
		S32 local_parcel_id,
		const LLHost& host) :
		mOwnerID(owner_id),
		mLocalID(local_parcel_id),
		mHost(host) {}
};

//
// Methods
//
LLViewerParcelMgr::LLViewerParcelMgr()
:	mSelected(FALSE),
	mRequestResult(0),
	mWestSouth(),
	mEastNorth(),
	mSelectedDwell(DWELL_NAN),
	mAgentParcelSequenceID(-1),
	mHoverRequestResult(0),
	mHoverWestSouth(),
	mHoverEastNorth(),
	mTeleportInProgressPosition(),
	mRenderCollision(FALSE),
	mRenderSelection(TRUE),
	mCollisionBanned(0),
	mCollisionTimer(),
// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
	mCollisionRegionHandle(0),
	mCollisionUpdateSignal(NULL),
// [/SL:KB]
	mMediaParcelId(0),
	mMediaRegionId(0)
{
	mCurrentParcel = new LLParcel();
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
	mFloatingParcelSelection = new LLParcelSelection(mCurrentParcel);

	mAgentParcel = new LLParcel();
	mHoverParcel = new LLParcel();
	mCollisionParcel = new LLParcel();

// <FS:CR> Aurora Sim
	// Max region size on Aurora-Sim, 8192, just creating larger buffers, it will still work on Second Life and Opensim
	F32 region_size = 8192.f;

	mParcelsPerEdge = S32(	region_size / PARCEL_GRID_STEP_METERS );
	//mParcelsPerEdge = S32(	REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
// </FS:CR> Aurora Sim
	mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
	resetSegments(mHighlightSegments);

// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
	mCollisionBitmap = new U8[getCollisionBitmapSize()];
	memset(mCollisionBitmap, 0, getCollisionBitmapSize());
// [/SL:KB]

	mCollisionSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
	resetSegments(mCollisionSegments);

	// JC: Resolved a merge conflict here, eliminated
	// mBlockedImage->setAddressMode(LLTexUnit::TAM_WRAP);
	// because it is done in llviewertexturelist.cpp
	mBlockedImage = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryLines.png", FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_UI);
	mPassImage = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png", FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_UI);

	S32 overlay_size = mParcelsPerEdge * mParcelsPerEdge / PARCEL_OVERLAY_CHUNKS;
	sPackedOverlay = new U8[overlay_size];

	mAgentParcelOverlay = new U8[mParcelsPerEdge * mParcelsPerEdge];
	S32 i;
	for (i = 0; i < mParcelsPerEdge * mParcelsPerEdge; i++)
	{
		mAgentParcelOverlay[i] = 0;
	}

// <FS:CR> Aurora Sim
	mParcelsPerEdge = S32(	REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
// </FS:CR> Aurora Sim

	mTeleportInProgress = TRUE; // the initial parcel update is treated like teleport
}

// <FS:CR> Aurora Sim
void LLViewerParcelMgr::init(F32 region_size)
{
	mParcelsPerEdge = S32(	region_size / PARCEL_GRID_STEP_METERS );
}
// </FS:CR> Aurora Sim

LLViewerParcelMgr::~LLViewerParcelMgr()
{
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = NULL;

	mFloatingParcelSelection->setParcel(NULL);
	mFloatingParcelSelection = NULL;

	delete mCurrentParcel;
	mCurrentParcel = NULL;

	delete mAgentParcel;
	mAgentParcel = NULL;

	delete mCollisionParcel;
	mCollisionParcel = NULL;

	delete mHoverParcel;
	mHoverParcel = NULL;

	delete[] mHighlightSegments;
	mHighlightSegments = NULL;

// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
	delete[] mCollisionBitmap;
	mCollisionBitmap = NULL;
// [/SL:KB]

	delete[] mCollisionSegments;
	mCollisionSegments = NULL;

	delete[] sPackedOverlay;
	sPackedOverlay = NULL;

	delete[] mAgentParcelOverlay;
	mAgentParcelOverlay = NULL;

	sBlockedImage = NULL;
	sPassImage = NULL;
}

void LLViewerParcelMgr::dump()
{
	LL_INFOS() << "Parcel Manager Dump" << LL_ENDL;
	LL_INFOS() << "mSelected " << S32(mSelected) << LL_ENDL;
	LL_INFOS() << "Selected parcel: " << LL_ENDL;
	LL_INFOS() << mWestSouth << " to " << mEastNorth << LL_ENDL;
	mCurrentParcel->dump();
	LL_INFOS() << "banning " << mCurrentParcel->mBanList.size() << LL_ENDL;
	
	LLAccessEntry::map::const_iterator cit = mCurrentParcel->mBanList.begin();
	LLAccessEntry::map::const_iterator end = mCurrentParcel->mBanList.end();
	for ( ; cit != end; ++cit)
	{
		LL_INFOS() << "ban id " << (*cit).first << LL_ENDL;
	}
	LL_INFOS() << "Hover parcel:" << LL_ENDL;
	mHoverParcel->dump();
	LL_INFOS() << "Agent parcel:" << LL_ENDL;
	mAgentParcel->dump();
}


LLViewerRegion* LLViewerParcelMgr::getSelectionRegion()
{
	return LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
}


void LLViewerParcelMgr::getDisplayInfo(S32* area_out, S32* claim_out,
									   S32* rent_out,
									   BOOL* for_sale_out,
									   F32* dwell_out)
{
	S32 area = 0;
	S32 price = 0;
	S32 rent = 0;
	BOOL for_sale = FALSE;
	F32 dwell = DWELL_NAN;

	if (mSelected)
	{
		if (mCurrentParcelSelection->mSelectedMultipleOwners)
		{
			area = mCurrentParcelSelection->getClaimableArea();
		}
		else
		{
			area = getSelectedArea();
		}

		if (mCurrentParcel->getForSale())
		{
			price = mCurrentParcel->getSalePrice();
			for_sale = TRUE;
		}
		else
		{
			price = area * mCurrentParcel->getClaimPricePerMeter();
			for_sale = FALSE;
		}

		rent = mCurrentParcel->getTotalRent();

		dwell = mSelectedDwell;
	}

	*area_out = area;
	*claim_out = price;
	*rent_out = rent;
	*for_sale_out = for_sale;
	*dwell_out = dwell;
}

S32 LLViewerParcelMgr::getSelectedArea() const
{
	S32 rv = 0;
	if(mSelected && mCurrentParcel && mCurrentParcelSelection->mWholeParcelSelected)
	{
		rv = mCurrentParcel->getArea();
	}
	else if(mSelected)
	{
		F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX];
		F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY];
		F32 area = (F32)(width * height);
		rv = ll_round(area);
	}
	return rv;
}

void LLViewerParcelMgr::resetSegments(U8* segments)
{
	S32 i;
	S32 count = (mParcelsPerEdge+1)*(mParcelsPerEdge+1);
	for (i = 0; i < count; i++)
	{
		segments[i] = 0x0;
	}
}


void LLViewerParcelMgr::writeHighlightSegments(F32 west, F32 south, F32 east,
											   F32 north)
{
	S32 x, y;
	S32 min_x = ll_round( west / PARCEL_GRID_STEP_METERS );
	S32 max_x = ll_round( east / PARCEL_GRID_STEP_METERS );
	S32 min_y = ll_round( south / PARCEL_GRID_STEP_METERS );
	S32 max_y = ll_round( north / PARCEL_GRID_STEP_METERS );

	const S32 STRIDE = mParcelsPerEdge+1;

	// south edge
	y = min_y;
	for (x = min_x; x < max_x; x++)
	{
		// exclusive OR means that writing to this segment twice
		// will turn it off
		mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
	}

	// west edge
	x = min_x;
	for (y = min_y; y < max_y; y++)
	{
		mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
	}

	// north edge - draw the south border on the y+1'th cell,
	// which given C-style arrays, is item foo[max_y]
	y = max_y;
	for (x = min_x; x < max_x; x++)
	{
		mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
	}

	// east edge - draw west border on x+1'th cell
	x = max_x;
	for (y = min_y; y < max_y; y++)
	{
		mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
	}
}


void LLViewerParcelMgr::writeSegmentsFromBitmap(U8* bitmap, U8* segments)
{
	S32 x;
	S32 y;
	const S32 IN_STRIDE = mParcelsPerEdge;
	const S32 OUT_STRIDE = mParcelsPerEdge+1;

	for (y = 0; y < IN_STRIDE; y++)
	{
		x = 0;
		while( x < IN_STRIDE )
		{
			U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];

			S32 bit;
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit) )
				{
					S32 out = x+y*OUT_STRIDE;

					// This and one above it
					segments[out]            ^= SOUTH_MASK;
					segments[out+OUT_STRIDE] ^= SOUTH_MASK;

					// This and one to the right
					segments[out]   ^= WEST_MASK;
					segments[out+1] ^= WEST_MASK;
				}
				x++;
			}
		}
	}
}


void LLViewerParcelMgr::writeAgentParcelFromBitmap(U8* bitmap)
{
	S32 x;
	S32 y;
	const S32 IN_STRIDE = mParcelsPerEdge;

	for (y = 0; y < IN_STRIDE; y++)
	{
		x = 0;
		while( x < IN_STRIDE )
		{
			U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];

			S32 bit;
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit) )
				{
					mAgentParcelOverlay[x+y*IN_STRIDE] = 1;
				}
				else
				{
					mAgentParcelOverlay[x+y*IN_STRIDE] = 0;
				}
				x++;
			}
		}
	}
}


// Given a point, find the PARCEL_GRID_STEP x PARCEL_GRID_STEP block
// containing it and select that.
LLParcelSelectionHandle LLViewerParcelMgr::selectParcelAt(const LLVector3d& pos_global)
{
	LLVector3d southwest = pos_global;
	LLVector3d northeast = pos_global;

	southwest -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
	southwest.mdV[VX] = ll_round( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	southwest.mdV[VY] = ll_round( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );

	northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
	northeast.mdV[VX] = ll_round( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	northeast.mdV[VY] = ll_round( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );

	// Snap to parcel
	return selectLand( southwest, northeast, TRUE );
}


// Tries to select the parcel inside the rectangle
LLParcelSelectionHandle LLViewerParcelMgr::selectParcelInRectangle()
{
	return selectLand(mWestSouth, mEastNorth, TRUE);
}


void LLViewerParcelMgr::selectCollisionParcel()
{
	// BUG: Claim to be in the agent's region
// <FS:CR> Aurora Sim
	//mWestSouth = gAgent.getRegion()->getOriginGlobal();
	//mEastNorth = mWestSouth;
	//mEastNorth += LLVector3d(PARCEL_GRID_STEP_METERS, PARCEL_GRID_STEP_METERS, 0.0);
	mWestSouth = getSelectionRegion()->getOriginGlobal();
	mEastNorth = mWestSouth;
	mEastNorth += LLVector3d((getSelectionRegion()->getWidth() / REGION_WIDTH_METERS) * PARCEL_GRID_STEP_METERS, 
		                     (getSelectionRegion()->getWidth() / REGION_WIDTH_METERS) * PARCEL_GRID_STEP_METERS, 0.0);
// </FS:CR> Aurora Sim

	// BUG: must be in the sim you are in
	LLMessageSystem *msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ParcelPropertiesRequestByID);
	msg->nextBlockFast(_PREHASH_AgentID);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
	msg->nextBlockFast(_PREHASH_ParcelData);
	msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
	msg->addS32Fast(_PREHASH_LocalID, mCollisionParcel->getLocalID() );
	gAgent.sendReliableMessage();

	mRequestResult = PARCEL_RESULT_NO_DATA;

	// Hack: Copy some data over temporarily
	mCurrentParcel->setName( mCollisionParcel->getName() );
	mCurrentParcel->setDesc( mCollisionParcel->getDesc() );
	mCurrentParcel->setPassPrice(mCollisionParcel->getPassPrice());
	mCurrentParcel->setPassHours(mCollisionParcel->getPassHours());

	// clear the list of segments to prevent flashing
	resetSegments(mHighlightSegments);

	mFloatingParcelSelection->setParcel(mCurrentParcel);
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

	mSelected = TRUE;
	mCurrentParcelSelection->mWholeParcelSelected = TRUE;
	notifyObservers();
	return;
}


// snap_selection = auto-select the hit parcel, if there is exactly one
LLParcelSelectionHandle LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
								   BOOL snap_selection)
{
	sanitize_corners( corner1, corner2, mWestSouth, mEastNorth );

	// ...x isn't more than one meter away
	F32 delta_x = getSelectionWidth();
	if (delta_x * delta_x <= 1.f * 1.f)
	{
		mSelected = FALSE;
		notifyObservers();
		return NULL;
	}

	// ...y isn't more than one meter away
	F32 delta_y = getSelectionHeight();
	if (delta_y * delta_y <= 1.f * 1.f)
	{
		mSelected = FALSE;
		notifyObservers();
		return NULL;
	}

	// Can't select across region boundary
	// We need to pull in the upper right corner by a little bit to allow
	// selection up to the x = 256 or y = 256 edge.
	LLVector3d east_north_region_check( mEastNorth );
	east_north_region_check.mdV[VX] -= 0.5;
	east_north_region_check.mdV[VY] -= 0.5;

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mWestSouth);
	LLViewerRegion *region_other = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );

	if(!region)
	{
		// just in case they somehow selected no land.
		mSelected = FALSE;
		return NULL;
	}

	if (region != region_other)
	{
		LLNotificationsUtil::add("CantSelectLandFromMultipleRegions");
		mSelected = FALSE;
		notifyObservers();
		return NULL;
	}

	// Build region global copies of corners
	LLVector3 wsb_region = region->getPosRegionFromGlobal( mWestSouth );
	LLVector3 ent_region = region->getPosRegionFromGlobal( mEastNorth );

	// Send request message
	LLMessageSystem *msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
	msg->nextBlockFast(_PREHASH_ParcelData);
	msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
	msg->addF32Fast(_PREHASH_West,  wsb_region.mV[VX] );
	msg->addF32Fast(_PREHASH_South, wsb_region.mV[VY] );
	msg->addF32Fast(_PREHASH_East,  ent_region.mV[VX] );
	msg->addF32Fast(_PREHASH_North, ent_region.mV[VY] );
	msg->addBOOL("SnapSelection", snap_selection);
	msg->sendReliable( region->getHost() );

	mRequestResult = PARCEL_RESULT_NO_DATA;

	mFloatingParcelSelection->setParcel(mCurrentParcel);
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

	mSelected = TRUE;
	mCurrentParcelSelection->mWholeParcelSelected = snap_selection;
	notifyObservers();
	return mCurrentParcelSelection;
}

void LLViewerParcelMgr::deselectUnused()
{
	// no more outstanding references to this selection, other than our own
	if (mCurrentParcelSelection->getNumRefs() == 1 && mFloatingParcelSelection->getNumRefs() == 1)
	{
		deselectLand();
	}
}

void LLViewerParcelMgr::deselectLand()
{
	if (mSelected)
	{
		mSelected = FALSE;

		// Invalidate the selected parcel
		mCurrentParcel->setLocalID(-1);
		mCurrentParcel->mAccessList.clear();
		mCurrentParcel->mBanList.clear();
		//mCurrentParcel->mRenterList.reset();

		mSelectedDwell = DWELL_NAN;

		// invalidate parcel selection so that existing users of this selection can clean up
		mCurrentParcelSelection->setParcel(NULL);
		mFloatingParcelSelection->setParcel(NULL);
		// create new parcel selection
		mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

		notifyObservers(); // Notify observers *after* changing the parcel selection
	}
}


void LLViewerParcelMgr::addObserver(LLParcelObserver* observer)
{
	mObservers.push_back(observer);
}


void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
{
	vector_replace_with_last(mObservers, observer);
}


// Call this method when it's time to update everyone on a new state.
// Copy the list because an observer could respond by removing itself
// from the list.
void LLViewerParcelMgr::notifyObservers()
{
	std::vector<LLParcelObserver*> observers;
	S32 count = mObservers.size();
	S32 i;
	for(i = 0; i < count; ++i)
	{
		observers.push_back(mObservers.at(i));
	}
	for(i = 0; i < count; ++i)
	{
		observers.at(i)->changed();
	}
}


//
// ACCESSORS
//
BOOL LLViewerParcelMgr::selectionEmpty() const
{
	return !mSelected;
}


LLParcelSelectionHandle LLViewerParcelMgr::getParcelSelection() const
{
	return mCurrentParcelSelection;
}

LLParcelSelectionHandle LLViewerParcelMgr::getFloatingParcelSelection() const
{
	return mFloatingParcelSelection;
}

LLParcel *LLViewerParcelMgr::getAgentParcel() const
{
	return mAgentParcel;
}


LLParcel * LLViewerParcelMgr::getAgentOrSelectedParcel() const
{
    LLParcel *parcel(nullptr);

    LLParcelSelectionHandle sel_handle(getFloatingParcelSelection());
    if (sel_handle)
    {
        LLParcelSelection *selection(sel_handle.get());
        if (selection)
        {
            parcel = selection->getParcel();
            if (parcel && (parcel->getLocalID() == INVALID_PARCEL_ID))
            {
                parcel = NULL;
            }
        }
    }

    if (!parcel)
        parcel = LLViewerParcelMgr::instance().getAgentParcel();

    return parcel;
}

// Return whether the agent can build on the land they are on
bool LLViewerParcelMgr::allowAgentBuild() const
{
	// <FS> Rez under land group
	//if (mAgentParcel)
	//{
	//	return (gAgent.isGodlike() ||
	//			(mAgentParcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID())) ||
	//			(isParcelOwnedByAgent(mAgentParcel, GP_LAND_ALLOW_CREATE)));
	//}
	//else
	//{
	//	return gAgent.isGodlike();
	//}
	if (gAgent.isGodlike())
	{
		return true;
	}
	else if (!mAgentParcel)
	{
		return false;
	}
	return (mAgentParcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID()) ||
			gAgent.hasPowerInGroup(mAgentParcel->getGroupID(), GP_LAND_ALLOW_CREATE));
	// </FS>
}

// Return whether anyone can build on the given parcel
bool LLViewerParcelMgr::allowAgentBuild(const LLParcel* parcel) const
{
	return parcel->getAllowModify();
}

bool LLViewerParcelMgr::allowAgentVoice() const
{
	return allowAgentVoice(gAgent.getRegion(), mAgentParcel);
}

bool LLViewerParcelMgr::allowAgentVoice(const LLViewerRegion* region, const LLParcel* parcel) const
{
	return region && region->isVoiceEnabled()
		&& parcel	&& parcel->getParcelFlagAllowVoice();
}

bool LLViewerParcelMgr::allowAgentFly(const LLViewerRegion* region, const LLParcel* parcel) const
{
	return region && !region->getBlockFly()
		&& parcel && parcel->getAllowFly();
}

// Can the agent be pushed around by LLPushObject?
bool LLViewerParcelMgr::allowAgentPush(const LLViewerRegion* region, const LLParcel* parcel) const
{
	return region && !region->getRestrictPushObject()
		&& parcel && !parcel->getRestrictPushObject();
}

bool LLViewerParcelMgr::allowAgentScripts(const LLViewerRegion* region, const LLParcel* parcel) const
{
	// *NOTE: This code does not take into account group-owned parcels
	// and the flag to allow group-owned scripted objects to run.
	// This mirrors the traditional menu bar parcel icon code, but is not
	// technically correct.
	return region
		&& !region->getRegionFlag(REGION_FLAGS_SKIP_SCRIPTS)
		&& !region->getRegionFlag(REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
		&& parcel
		&& parcel->getAllowOtherScripts();
}

bool LLViewerParcelMgr::allowAgentDamage(const LLViewerRegion* region, const LLParcel* parcel) const
{
	return (region && region->getAllowDamage())
		|| (parcel && parcel->getAllowDamage());
}

BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwned( pos_region );
}

BOOL LLViewerParcelMgr::isOwnedSelfAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwnedSelf( pos_region );
}

BOOL LLViewerParcelMgr::isOwnedOtherAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwnedOther( pos_region );
}

BOOL LLViewerParcelMgr::isSoundLocal(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isSoundLocal( pos_region );
}

BOOL LLViewerParcelMgr::canHearSound(const LLVector3d &pos_global) const
{
	BOOL in_agent_parcel = inAgentParcel(pos_global);

	if (in_agent_parcel)
	{
		// In same parcel as the agent
		return TRUE;
	}
	else
	{
		if (LLViewerParcelMgr::getInstance()->getAgentParcel()->getSoundLocal())
		{
			// Not in same parcel, and agent parcel only has local sound
			return FALSE;
		}
		else if (LLViewerParcelMgr::getInstance()->isSoundLocal(pos_global))
		{
			// Not in same parcel, and target parcel only has local sound
			return FALSE;
		}
		else
		{
			// Not in same parcel, but neither are local sound
			return TRUE;
		}
	}
}


BOOL LLViewerParcelMgr::inAgentParcel(const LLVector3d &pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(pos_global);
	LLViewerRegion* agent_region = gAgent.getRegion();
	if (!region || !agent_region)
		return FALSE;

	if (region != agent_region)
	{
		// Can't be in the agent parcel if you're not in the same region.
		return FALSE;
	}

	LLVector3 pos_region = agent_region->getPosRegionFromGlobal(pos_global);
	S32 row =    S32(pos_region.mV[VY] / PARCEL_GRID_STEP_METERS);
	S32 column = S32(pos_region.mV[VX] / PARCEL_GRID_STEP_METERS);

	if (mAgentParcelOverlay[row*mParcelsPerEdge + column])
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// Returns NULL when there is no valid data.
LLParcel* LLViewerParcelMgr::getHoverParcel() const
{
	if (mHoverRequestResult == PARCEL_RESULT_SUCCESS)
	{
		return mHoverParcel;
	}
	else
	{
		return NULL;
	}
}

// Returns NULL when there is no valid data.
LLParcel* LLViewerParcelMgr::getCollisionParcel() const
{
	if (mRenderCollision)
	{
		return mCollisionParcel;
	}
	else
	{
		return NULL;
	}
}

//
// UTILITIES
//

void LLViewerParcelMgr::render()
{
	if (mSelected && mRenderSelection && gSavedSettings.getBOOL("RenderParcelSelection"))
	{
		// Rendering is done in agent-coordinates, so need to supply
		// an appropriate offset to the render code.
		LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mWestSouth);
		if (!regionp) return;

		renderHighlightSegments(mHighlightSegments, regionp);
	}
}


void LLViewerParcelMgr::renderParcelCollision()
{
	// check for expiration
	if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS)
	{
		mRenderCollision = FALSE;
	}

	if (mRenderCollision && gSavedSettings.getBOOL("ShowBanLines"))
	{
		LLViewerRegion* regionp = gAgent.getRegion();
		if (regionp)
		{
			BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST);
			renderCollisionSegments(mCollisionSegments, use_pass, regionp);
		}
	}
}


void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
{
	if (!mSelected)
	{
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region) return;

	// <FS:Ansariel> FIRE-17280: Requesting Experience access allow & block list breaks OpenSim
	if (!region->isCapabilityAvailable("RegionExperiences"))
	{
		flags &= ~(AL_ALLOW_EXPERIENCE | AL_BLOCK_EXPERIENCE);
	}
	// </FS:Ansariel>

	LLMessageSystem *msg = gMessageSystem;


	if (flags & AL_BAN) 
	{
		mCurrentParcel->mBanList.clear();
	}
	if (flags & AL_ACCESS) 
	{
		mCurrentParcel->mAccessList.clear();
	}		
	if (flags & AL_ALLOW_EXPERIENCE) 
	{
		mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_ALLOWED);
	}
	if (flags & AL_BLOCK_EXPERIENCE) 
	{
		mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_BLOCKED);
	}		

	// Only the headers differ
	msg->newMessageFast(_PREHASH_ParcelAccessListRequest);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->nextBlockFast(_PREHASH_Data);
	msg->addS32Fast(_PREHASH_SequenceID, 0);
	msg->addU32Fast(_PREHASH_Flags, flags);
	msg->addS32("LocalID", mCurrentParcel->getLocalID() );
	msg->sendReliable( region->getHost() );
}


void LLViewerParcelMgr::sendParcelDwellRequest()
{
	if (!mSelected)
	{
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region) return;

	LLMessageSystem *msg = gMessageSystem;

	// Only the headers differ
	msg->newMessage("ParcelDwellRequest");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID() );
	msg->addUUID("SessionID", gAgent.getSessionID());
	msg->nextBlock("Data");
	msg->addS32("LocalID", mCurrentParcel->getLocalID());
	msg->addUUID("ParcelID", LLUUID::null);	// filled in on simulator
	msg->sendReliable( region->getHost() );
}


void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id)
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotSetLandOwnerNothingSelected");
		return;
	}

	LL_INFOS("ParcelMgr") << "Claiming " << mWestSouth << " to " << mEastNorth << LL_ENDL;

	// BUG: Only works for the region containing mWestSouthBottom
	LLVector3d east_north_region_check( mEastNorth );
	east_north_region_check.mdV[VX] -= 0.5;
	east_north_region_check.mdV[VY] -= 0.5;

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		// TODO: Add a force owner version of this alert.
		LLNotificationsUtil::add("CannotContentifyNoRegion");
		return;
	}

	// BUG: Make work for cross-region selections
	LLViewerRegion *region2 = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );
	if (region != region2)
	{
		LLNotificationsUtil::add("CannotSetLandOwnerMultipleRegions");
		return;
	}

	LL_INFOS("ParcelMgr") << "Region " << region->getOriginGlobal() << LL_ENDL;

	LLSD payload;
	payload["owner_id"] = owner_id;
	payload["parcel_local_id"] = mCurrentParcel->getLocalID();
	payload["region_host"] = region->getHost().getIPandPort();
	LLNotification::Params params("ForceOwnerAuctionWarning");
	params.payload(payload).functor.function(callback_god_force_owner);

	if(mCurrentParcel->getAuctionID())
	{
		LLNotifications::instance().add(params);
	}
	else
	{
		LLNotifications::instance().forceResponse(params, 0);
	}
}

bool callback_god_force_owner(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	if(0 == option)
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("ParcelGodForceOwner");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->nextBlock("Data");
		msg->addUUID("OwnerID", notification["payload"]["owner_id"].asUUID());
		msg->addS32( "LocalID", notification["payload"]["parcel_local_id"].asInteger());
		msg->sendReliable(LLHost(notification["payload"]["region_host"].asString()));
	}
	return false;
}

void LLViewerParcelMgr::sendParcelGodForceToContent()
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotContentifyNothingSelected");
		return;
	}
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotificationsUtil::add("CannotContentifyNoRegion");
		return;
	}

	LLMessageSystem* msg = gMessageSystem;
	msg->newMessage("ParcelGodMarkAsContent");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID());
	msg->addUUID("SessionID", gAgent.getSessionID());
	msg->nextBlock("ParcelData");
	msg->addS32("LocalID", mCurrentParcel->getLocalID());
	msg->sendReliable(region->getHost());
}

void LLViewerParcelMgr::sendParcelRelease()
{
	if (!mSelected)
	{
        LLNotificationsUtil::add("CannotReleaseLandNothingSelected");
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotificationsUtil::add("CannotReleaseLandNoRegion");
		return;
	}

	//U32 flags = PR_NONE;
	//if (god_force) flags |= PR_GOD_FORCE;

	LLMessageSystem* msg = gMessageSystem;
	msg->newMessage("ParcelRelease");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID() );
	msg->addUUID("SessionID", gAgent.getSessionID() );
	msg->nextBlock("Data");
	msg->addS32("LocalID", mCurrentParcel->getLocalID() );
	//msg->addU32("Flags", flags);
	msg->sendReliable( region->getHost() );

	// Blitz selection, since the parcel might be non-rectangular, and
	// we won't have appropriate parcel information.
	deselectLand();
}

class LLViewerParcelMgr::ParcelBuyInfo
{
public:
	LLUUID	mAgent;
	LLUUID	mSession;
	LLUUID	mGroup;
	BOOL	mIsGroupOwned;
	BOOL	mRemoveContribution;
	BOOL	mIsClaim;
	LLHost	mHost;
	
	// for parcel buys
	S32		mParcelID;
	S32		mPrice;
	S32		mArea;

	// for land claims
	F32		mWest;
	F32		mSouth;
	F32		mEast;
	F32		mNorth;
};

LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy(
	const LLUUID& agent_id,
	const LLUUID& session_id,
	const LLUUID& group_id,
	BOOL is_group_owned,
	BOOL is_claim,
	BOOL remove_contribution)
{
	if (!mSelected || !mCurrentParcel)
	{
		LLNotificationsUtil::add("CannotBuyLandNothingSelected");
		return NULL;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotificationsUtil::add("CannotBuyLandNoRegion");
		return NULL;
	}
	
	if (is_claim)
	{
		LL_INFOS("ParcelMgr") << "Claiming " << mWestSouth << " to " << mEastNorth << LL_ENDL;
		LL_INFOS("ParcelMgr") << "Region " << region->getOriginGlobal() << LL_ENDL;

		// BUG: Only works for the region containing mWestSouthBottom
		LLVector3d east_north_region_check( mEastNorth );
		east_north_region_check.mdV[VX] -= 0.5;
		east_north_region_check.mdV[VY] -= 0.5;

		LLViewerRegion *region2 = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );

		if (region != region2)
		{
			LLNotificationsUtil::add("CantBuyLandAcrossMultipleRegions");
			return NULL;
		}
	}
	
	
	ParcelBuyInfo* info = new ParcelBuyInfo;
	
	info->mAgent = agent_id;
	info->mSession = session_id;
	info->mGroup = group_id;
	info->mIsGroupOwned = is_group_owned;
	info->mIsClaim = is_claim;
	info->mRemoveContribution = remove_contribution;
	info->mHost = region->getHost();
	info->mPrice = mCurrentParcel->getSalePrice();
	info->mArea = mCurrentParcel->getArea();
	
	if (!is_claim)
	{
		info->mParcelID = mCurrentParcel->getLocalID();
	}
	else
	{
		// BUG: Make work for cross-region selections
		LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
		LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
		
		info->mWest		= west_south_bottom_region.mV[VX];
		info->mSouth	= west_south_bottom_region.mV[VY];
		info->mEast		= east_north_top_region.mV[VX];
		info->mNorth	= east_north_top_region.mV[VY];
	}
	
	return info;
}

void LLViewerParcelMgr::sendParcelBuy(ParcelBuyInfo* info)
{
	// send the message
	LLMessageSystem* msg = gMessageSystem;
	msg->newMessage(info->mIsClaim ? "ParcelClaim" : "ParcelBuy");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", info->mAgent);
	msg->addUUID("SessionID", info->mSession);
	msg->nextBlock("Data");
	msg->addUUID("GroupID", info->mGroup);
	msg->addBOOL("IsGroupOwned", info->mIsGroupOwned);
	if (!info->mIsClaim)
	{
		msg->addBOOL("RemoveContribution", info->mRemoveContribution);
		msg->addS32("LocalID", info->mParcelID);
	}
	msg->addBOOL("Final", TRUE);	// don't allow escrow buys
	if (info->mIsClaim)
	{
		msg->nextBlock("ParcelData");
		msg->addF32("West",  info->mWest);
		msg->addF32("South", info->mSouth);
		msg->addF32("East",  info->mEast);
		msg->addF32("North", info->mNorth);
	}
	else // ParcelBuy
	{
		msg->nextBlock("ParcelData");
		msg->addS32("Price",info->mPrice);
		msg->addS32("Area",info->mArea);
	}
	msg->sendReliable(info->mHost);
}

void LLViewerParcelMgr::deleteParcelBuy(ParcelBuyInfo* *info)
{
	// Must be here because ParcelBuyInfo is local to this .cpp file
	delete *info;
	*info = NULL;
}

void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id)
{
	if (!mSelected || !mCurrentParcel)
	{
		LLNotificationsUtil::add("CannotDeedLandNothingSelected");
		return;
	}
	if(group_id.isNull())
	{
		LLNotificationsUtil::add("CannotDeedLandNoGroup");
		return;
	}
	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotificationsUtil::add("CannotDeedLandNoRegion");
		return;
	}

	LLMessageSystem* msg = gMessageSystem;
	msg->newMessage("ParcelDeedToGroup");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID() );
	msg->addUUID("SessionID", gAgent.getSessionID() );
	msg->nextBlock("Data");
	msg->addUUID("GroupID", group_id );
	msg->addS32("LocalID", mCurrentParcel->getLocalID() );
	//msg->addU32("JoinNeighbors", join);
	msg->sendReliable( region->getHost() );
}


/*
// *NOTE: We cannot easily make landmarks at global positions because
// global positions probably refer to a sim/local combination which
// can move over time. We could implement this by looking up the
// region global x,y, but it's easier to take it out for now.
void LLViewerParcelMgr::makeLandmarkAtSelection()
{
	// Don't create for parcels you don't own
	if (gAgent.getID() != mCurrentParcel->getOwnerID())
	{
		return;
	}

	LLVector3d global_center(mWestSouth);
	global_center += mEastNorth;
	global_center *= 0.5f;

	LLViewerRegion* region;
	region = LLWorld::getInstance()->getRegionFromPosGlobal(global_center);

	LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
	LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );

	std::string name("My Land");
	std::string buffer;
	S32 pos_x = (S32)floor((west_south_bottom_region.mV[VX] + east_north_top_region.mV[VX]) / 2.0f);
	S32 pos_y = (S32)floor((west_south_bottom_region.mV[VY] + east_north_top_region.mV[VY]) / 2.0f);
	buffer = llformat("%s in %s (%d, %d)",
			name.c_str(),
			region->getName().c_str(),
			pos_x, pos_y);
	name.assign(buffer);

	create_landmark(name, "Claimed land", global_center);
}
*/

const std::string& LLViewerParcelMgr::getAgentParcelName() const
{
	return mAgentParcel->getName();
}


const S32 LLViewerParcelMgr::getAgentParcelId() const
{
    if (mAgentParcel)
        return mAgentParcel->getLocalID();
    return INVALID_PARCEL_ID;
}

void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_agent_region)
{
	if(!parcel) 
        return;

	LLViewerRegion *region = use_agent_region ? gAgent.getRegion() : LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region) 
        return;

	LLSD body;
	std::string url = region->getCapability("ParcelPropertiesUpdate");
	if (!url.empty())
	{
		// request new properties update from simulator
		U32 message_flags = 0x01;
		body["flags"] = ll_sd_from_U32(message_flags);
		parcel->packMessage(body);
		LL_INFOS("ParcelMgr") << "Sending parcel properties update via capability to: "
			<< url << LL_ENDL;

        LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body,
            "Parcel Properties sent to sim.", "Parcel Properties failed to send to sim.");
	}
	else
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessageFast(_PREHASH_ParcelPropertiesUpdate);
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID,	gAgent.getID() );
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
		msg->nextBlockFast(_PREHASH_ParcelData);
		msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );

		U32 message_flags = 0x01;
		msg->addU32("Flags", message_flags);

		parcel->packMessage(msg);

		msg->sendReliable( region->getHost() );
	}
}


void LLViewerParcelMgr::setHoverParcel(const LLVector3d& pos)
{
	static U32 last_west, last_south;
	static LLUUID last_region;

	// only request parcel info if position has changed outside of the
	// last parcel grid step
	const U32 west_parcel_step = (U32) floor( pos.mdV[VX] / PARCEL_GRID_STEP_METERS );
	const U32 south_parcel_step = (U32) floor( pos.mdV[VY] / PARCEL_GRID_STEP_METERS );

	if ((west_parcel_step == last_west) && (south_parcel_step == last_south))
	{
		// We are staying in same segment
		return;
	}

	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos );
	if (!region)
	{
		return;
	}

	LLUUID region_id = region->getRegionID();
	LLVector3 pos_in_region = region->getPosRegionFromGlobal(pos);

	bool request_properties = false;
	if (region_id != last_region)
	{
		request_properties = true;
	}
	else
	{
		// Check if new position is in same parcel.
		// This check is not ideal, since it checks by way of straight lines.
		// So sometimes (small parcel in the middle of large one) it can
		// decide that parcel actually changed, but it still allows to 
		// reduce amount of requests significantly

		S32 west_parcel_local = (S32)(pos_in_region.mV[VX] / PARCEL_GRID_STEP_METERS);
		S32 south_parcel_local = (S32)(pos_in_region.mV[VY] / PARCEL_GRID_STEP_METERS);

		LLViewerParcelOverlay* overlay = region->getParcelOverlay();
		if (!overlay)
		{
			 request_properties = true;
		}
		while (!request_properties && west_parcel_step < last_west)
		{
			S32 segment_shift = last_west - west_parcel_step;
			request_properties = overlay->parcelLineFlags(south_parcel_local, west_parcel_local + segment_shift) & PARCEL_WEST_LINE;
			last_west--;
		}
		while (!request_properties && south_parcel_step < last_south)
		{
			S32 segment_shift = last_south - south_parcel_step;
			request_properties = overlay->parcelLineFlags(south_parcel_local + segment_shift, west_parcel_local) & PARCEL_SOUTH_LINE;
			last_south--;
		}
		// Note: could have just swapped values, reused first two 'while' and set last_south, last_west separately,
		// but this looks to be easier to understand/straightforward/less bulky
		while (!request_properties && west_parcel_step > last_west)
		{
			S32 segment_shift = west_parcel_step - last_west;
			request_properties = overlay->parcelLineFlags(south_parcel_local, west_parcel_local - segment_shift + 1) & PARCEL_WEST_LINE;
			last_west++;
		}
		while (!request_properties && south_parcel_step > last_south)
		{
			S32 segment_shift = south_parcel_step - last_south;
			request_properties = overlay->parcelLineFlags(south_parcel_local - segment_shift + 1, west_parcel_local) & PARCEL_SOUTH_LINE;
			last_south++;
		}

		// if (!request_properties) last_south and last_west will be equal to new values
	}

	if (request_properties)
	{
		last_west = west_parcel_step;
		last_south = south_parcel_step;
		last_region = region_id;

		LL_DEBUGS("ParcelMgr") << "Requesting parcel properties on hover, for " << pos << LL_ENDL;


		// Send a rectangle around the point.
		// This means the parcel sent back is at least a rectangle around the point,
		// which is more efficient for public land.  Fewer requests are sent.  JC
		F32 west = PARCEL_GRID_STEP_METERS * floor(pos_in_region.mV[VX] / PARCEL_GRID_STEP_METERS);
		F32 south = PARCEL_GRID_STEP_METERS * floor(pos_in_region.mV[VY] / PARCEL_GRID_STEP_METERS);

		F32 east = west + PARCEL_GRID_STEP_METERS;
		F32 north = south + PARCEL_GRID_STEP_METERS;

		// Send request message
		LLMessageSystem *msg = gMessageSystem;
		msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
		msg->nextBlockFast(_PREHASH_ParcelData);
		msg->addS32Fast(_PREHASH_SequenceID, HOVERED_PARCEL_SEQ_ID);
		msg->addF32Fast(_PREHASH_West, west);
		msg->addF32Fast(_PREHASH_South, south);
		msg->addF32Fast(_PREHASH_East, east);
		msg->addF32Fast(_PREHASH_North, north);
		msg->addBOOL("SnapSelection", FALSE);
		msg->sendReliable(region->getHost());

		mHoverRequestResult = PARCEL_RESULT_NO_DATA;
	}
}


// static
void LLViewerParcelMgr::processParcelOverlay(LLMessageSystem *msg, void **user)
{
	// Extract the packed overlay information
	S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data);

	if (packed_overlay_size <= 0)
	{
		LL_WARNS() << "Overlay size " << packed_overlay_size << LL_ENDL;
		return;
	}

// <FS:CR> Aurora Sim
	//S32 parcels_per_edge = LLViewerParcelMgr::getInstance()->mParcelsPerEdge;
	//S32 expected_size = parcels_per_edge * parcels_per_edge / PARCEL_OVERLAY_CHUNKS;
	S32 expected_size = 1024;
// </FS:CR> Aurora Sim
	if (packed_overlay_size != expected_size)
	{
		LL_WARNS() << "Got parcel overlay size " << packed_overlay_size
			<< " expecting " << expected_size << LL_ENDL;
		return;
	}

	S32 sequence_id;
	msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id);
	msg->getBinaryDataFast(
			_PREHASH_ParcelData,
			_PREHASH_Data,
			sPackedOverlay,
			expected_size);

	LLHost host = msg->getSender();
	LLViewerRegion *region = LLWorld::getInstance()->getRegion(host);
	if (region)
	{
		region->mParcelOverlay->uncompressLandOverlay( sequence_id, sPackedOverlay );
	}
}

// static
void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **user)
{
    S32		request_result;
    S32		sequence_id;
    BOOL	snap_selection = FALSE;
    S32		self_count = 0;
    S32		other_count = 0;
    S32		public_count = 0;
    S32		local_id;
    LLUUID	owner_id;
    BOOL	is_group_owned;
    U32 auction_id = 0;
    S32		claim_price_per_meter = 0;
    S32		rent_price_per_meter = 0;
    S32		claim_date = 0;
    LLVector3	aabb_min;
    LLVector3	aabb_max;
    S32		area = 0;
    S32		sw_max_prims = 0;
    S32		sw_total_prims = 0;
    //LLUUID	buyer_id;
    U8 status = 0;
    S32		max_prims = 0;
    S32		total_prims = 0;
    S32		owner_prims = 0;
    S32		group_prims = 0;
    S32		other_prims = 0;
    S32		selected_prims = 0;
    F32		parcel_prim_bonus = 1.f;
    BOOL	region_push_override = false;
    BOOL	region_deny_anonymous_override = false;
    BOOL	region_deny_identified_override = false; // Deprecated
    BOOL	region_deny_transacted_override = false; // Deprecated
    BOOL	region_deny_age_unverified_override = false;
    BOOL    region_allow_access_override = true;
    BOOL    region_allow_environment_override = true;
    S32     parcel_environment_version = 0;
    BOOL	agent_parcel_update = false; // updating previous(existing) agent parcel

    S32		other_clean_time = 0;

    LLViewerParcelMgr& parcel_mgr = LLViewerParcelMgr::instance();
// <FS:CR> Aurora Sim
	LLViewerRegion* msg_region = LLWorld::getInstance()->getRegion( msg->getSender() );
	if (msg_region)
	{
		parcel_mgr.mParcelsPerEdge = S32( msg_region->getWidth() / PARCEL_GRID_STEP_METERS );
	}
	else
	{
		if (!gAgent.getRegion())
		{
			return;
		}
		parcel_mgr.mParcelsPerEdge = S32( gAgent.getRegion()->getWidth() / PARCEL_GRID_STEP_METERS );
	}
// </FS:CR> Aurora Sim

    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_RequestResult, request_result);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id);

    if (request_result == PARCEL_RESULT_NO_DATA)
    {
        // no valid parcel data
        LL_INFOS("ParcelMgr") << "no valid parcel data" << LL_ENDL;
        return;
    }

    // Decide where the data will go.
    LLParcel* parcel = NULL;
    if (sequence_id == SELECTED_PARCEL_SEQ_ID)
    {
        // ...selected parcels report this sequence id
        parcel_mgr.mRequestResult = PARCEL_RESULT_SUCCESS;
        parcel = parcel_mgr.mCurrentParcel;
    }
    else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
    {
        parcel_mgr.mHoverRequestResult = PARCEL_RESULT_SUCCESS;
        parcel = parcel_mgr.mHoverParcel;
    }
    else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
        sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
        sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
    {
        parcel_mgr.mHoverRequestResult = PARCEL_RESULT_SUCCESS;
        parcel = parcel_mgr.mCollisionParcel;
    }
    else if (sequence_id == 0 || sequence_id > parcel_mgr.mAgentParcelSequenceID)
    {
        // new agent parcel
        parcel_mgr.mAgentParcelSequenceID = sequence_id;
        parcel = parcel_mgr.mAgentParcel;
    }
    else
    {
        LL_INFOS("ParcelMgr") << "out of order agent parcel sequence id " << sequence_id
            << " last good " << parcel_mgr.mAgentParcelSequenceID
            << LL_ENDL;
        return;
    }

    msg->getBOOL("ParcelData", "SnapSelection", snap_selection);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelfCount, self_count);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherCount, other_count);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_PublicCount, public_count);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_LocalID, local_id);
    msg->getUUIDFast(_PREHASH_ParcelData, _PREHASH_OwnerID, owner_id);
    msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_IsGroupOwned, is_group_owned);
    msg->getU32Fast(_PREHASH_ParcelData, _PREHASH_AuctionID, auction_id);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_ClaimDate, claim_date);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_ClaimPrice, claim_price_per_meter);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_RentPrice, rent_price_per_meter);
    msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMin, aabb_min);
    msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMax, aabb_max);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_Area, area);
    //msg->getUUIDFast(	_PREHASH_ParcelData, _PREHASH_BuyerID, buyer_id);
    msg->getU8("ParcelData", "Status", status);
    msg->getS32("ParcelData", "SimWideMaxPrims", sw_max_prims);
    msg->getS32("ParcelData", "SimWideTotalPrims", sw_total_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_MaxPrims, max_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_TotalPrims, total_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OwnerPrims, owner_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_GroupPrims, group_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherPrims, other_prims);
    msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelectedPrims, selected_prims);
    msg->getF32Fast(_PREHASH_ParcelData, _PREHASH_ParcelPrimBonus, parcel_prim_bonus);
    msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionPushOverride, region_push_override);
    msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyAnonymous, region_deny_anonymous_override);
    msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyIdentified, region_deny_identified_override); // Deprecated
    msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyTransacted, region_deny_transacted_override); // Deprecated
    if (msg->getNumberOfBlocksFast(_PREHASH_AgeVerificationBlock))
    {
        // this block was added later and may not be on older sims, so we have to test its existence first
        msg->getBOOLFast(_PREHASH_AgeVerificationBlock, _PREHASH_RegionDenyAgeUnverified, region_deny_age_unverified_override);
    }

    if (msg->getNumberOfBlocks(_PREHASH_RegionAllowAccessBlock))
    {
        msg->getBOOLFast(_PREHASH_RegionAllowAccessBlock, _PREHASH_RegionAllowAccessOverride, region_allow_access_override);
    }

    if (msg->getNumberOfBlocks(_PREHASH_ParcelEnvironmentBlock))
    {
        msg->getS32Fast(_PREHASH_ParcelEnvironmentBlock, _PREHASH_ParcelEnvironmentVersion, parcel_environment_version);
        msg->getBOOLFast(_PREHASH_ParcelEnvironmentBlock, _PREHASH_RegionAllowEnvironmentOverride, region_allow_environment_override);
    }

	msg->getS32("ParcelData", "OtherCleanTime", other_clean_time );

	LL_DEBUGS("ParcelMgr") << "Processing parcel " << local_id << " update, target(sequence): " << sequence_id << LL_ENDL;

	// Actually extract the data.
	if (parcel)
	{
        if (local_id == parcel_mgr.mAgentParcel->getLocalID())
        {
            // Parcels in different regions can have same ids.
            LLViewerRegion* parcel_region = LLWorld::getInstance()->getRegion(msg->getSender());
            LLViewerRegion* agent_region = gAgent.getRegion();
            if (parcel_region && agent_region && parcel_region->getRegionID() == agent_region->getRegionID())
            {
                // we got an updated version of agent parcel
                agent_parcel_update = true;
            }
        }

        S32 cur_parcel_environment_version = parcel->getParcelEnvironmentVersion();
        bool environment_changed = (cur_parcel_environment_version != parcel_environment_version);

		parcel->init(owner_id,
			FALSE, FALSE, FALSE,
			claim_date, claim_price_per_meter, rent_price_per_meter,
			area, other_prims, parcel_prim_bonus, is_group_owned);
		parcel->setLocalID(local_id);
		parcel->setAABBMin(aabb_min);
		parcel->setAABBMax(aabb_max);

		parcel->setAuctionID(auction_id);
		parcel->setOwnershipStatus((LLParcel::EOwnershipStatus)status);

		parcel->setSimWideMaxPrimCapacity(sw_max_prims);
		parcel->setSimWidePrimCount(sw_total_prims);
		parcel->setMaxPrimCapacity(max_prims);
		parcel->setOwnerPrimCount(owner_prims);
		parcel->setGroupPrimCount(group_prims);
		parcel->setOtherPrimCount(other_prims);
		parcel->setSelectedPrimCount(selected_prims);
		parcel->setParcelPrimBonus(parcel_prim_bonus);

		parcel->setCleanOtherTime(other_clean_time);
		parcel->setRegionPushOverride(region_push_override);
		parcel->setRegionDenyAnonymousOverride(region_deny_anonymous_override);
		parcel->setRegionDenyAgeUnverifiedOverride(region_deny_age_unverified_override);
        parcel->setRegionAllowAccessOverride(region_allow_access_override);
        parcel->setParcelEnvironmentVersion(cur_parcel_environment_version);
        parcel->setRegionAllowEnvironmentOverride(region_allow_environment_override);

		parcel->unpackMessage(msg);

		if (parcel == parcel_mgr.mAgentParcel)
		{
			// new agent parcel
			S32 bitmap_size =	parcel_mgr.mParcelsPerEdge
								* parcel_mgr.mParcelsPerEdge
								/ 8;
			U8* bitmap = new U8[ bitmap_size ];
			msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);

			parcel_mgr.writeAgentParcelFromBitmap(bitmap);
			delete[] bitmap;

			// Let interesting parties know about agent parcel change.
			LLViewerParcelMgr* instance = LLViewerParcelMgr::getInstance();

			if (instance->mTeleportInProgress)
			{
				if(instance->mTeleportInProgressPosition.isNull())
				{
					//initial update
					instance->mTeleportFinishedSignal(gAgent.getPositionGlobal(), false);
				}
				else
				{
					instance->mTeleportFinishedSignal(instance->mTeleportInProgressPosition, false);
				}
			}
            parcel->setParcelEnvironmentVersion(parcel_environment_version);
            LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL;

            // Notify anything that wants to know when the agent changes parcels
            gAgent.changeParcels();
            instance->mTeleportInProgress = FALSE;
        }
        else if (agent_parcel_update)
        {
            parcel->setParcelEnvironmentVersion(parcel_environment_version);
            // updated agent parcel
            parcel_mgr.mAgentParcel->unpackMessage(msg);
            if ((LLEnvironment::instance().isExtendedEnvironmentEnabled() && environment_changed))
            {
                LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL;
                LLEnvironment::instance().requestParcel(local_id);
            }
        }
	}

	// Handle updating selections, if necessary.
	if (sequence_id == SELECTED_PARCEL_SEQ_ID)
	{
		// Update selected counts
		parcel_mgr.mCurrentParcelSelection->mSelectedSelfCount = self_count;
		parcel_mgr.mCurrentParcelSelection->mSelectedOtherCount = other_count;
		parcel_mgr.mCurrentParcelSelection->mSelectedPublicCount = public_count;

		parcel_mgr.mCurrentParcelSelection->mSelectedMultipleOwners =
							(request_result == PARCEL_RESULT_MULTIPLE);

		// Select the whole parcel
		LLViewerRegion* region = LLWorld::getInstance()->getRegion( msg->getSender() );
		if (region)
		{
			if (!snap_selection)
			{
				// don't muck with the westsouth and eastnorth.
				// just highlight it
				LLVector3 west_south = region->getPosRegionFromGlobal(parcel_mgr.mWestSouth);
				LLVector3 east_north = region->getPosRegionFromGlobal(parcel_mgr.mEastNorth);

				parcel_mgr.resetSegments(parcel_mgr.mHighlightSegments);
				parcel_mgr.writeHighlightSegments(
								west_south.mV[VX],
								west_south.mV[VY],
								east_north.mV[VX],
								east_north.mV[VY] );
				parcel_mgr.mCurrentParcelSelection->mWholeParcelSelected = FALSE;
			}
			else if (0 == local_id)
			{
				// this is public land, just highlight the selection
				parcel_mgr.mWestSouth = region->getPosGlobalFromRegion( aabb_min );
				parcel_mgr.mEastNorth = region->getPosGlobalFromRegion( aabb_max );

				parcel_mgr.resetSegments(parcel_mgr.mHighlightSegments);
				parcel_mgr.writeHighlightSegments(
								aabb_min.mV[VX],
								aabb_min.mV[VY],
								aabb_max.mV[VX],
								aabb_max.mV[VY] );
				parcel_mgr.mCurrentParcelSelection->mWholeParcelSelected = TRUE;
			}
			else
			{
				parcel_mgr.mWestSouth = region->getPosGlobalFromRegion( aabb_min );
				parcel_mgr.mEastNorth = region->getPosGlobalFromRegion( aabb_max );

				// Owned land, highlight the boundaries
				S32 bitmap_size =	parcel_mgr.mParcelsPerEdge
									* parcel_mgr.mParcelsPerEdge
									/ 8;
				U8* bitmap = new U8[ bitmap_size ];
				msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);

				parcel_mgr.resetSegments(parcel_mgr.mHighlightSegments);
				parcel_mgr.writeSegmentsFromBitmap( bitmap, parcel_mgr.mHighlightSegments );

				delete[] bitmap;
				bitmap = NULL;

				parcel_mgr.mCurrentParcelSelection->mWholeParcelSelected = TRUE;
			}

			// Request access list information for this land
			parcel_mgr.sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_ALLOW_EXPERIENCE | AL_BLOCK_EXPERIENCE);

			// Request dwell for this land, if it's not public land.
			parcel_mgr.mSelectedDwell = DWELL_NAN;
			if (0 != local_id)
			{
				parcel_mgr.sendParcelDwellRequest();
			}

			parcel_mgr.mSelected = TRUE;
			parcel_mgr.notifyObservers();
		}
	}
	else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
			 sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID  ||
			 sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
	{
		// We're about to collide with this parcel
		parcel_mgr.mRenderCollision = TRUE;
		parcel_mgr.mCollisionTimer.reset();

		// Differentiate this parcel if we are banned from it.
		if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
		{
			parcel_mgr.mCollisionBanned = BA_BANNED;
		}
		else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID)
		{
			parcel_mgr.mCollisionBanned = BA_NOT_IN_GROUP;
		}
		else 
		{
			parcel_mgr.mCollisionBanned = BA_NOT_ON_LIST;

		}

//		S32 bitmap_size =	parcel_mgr.mParcelsPerEdge
//							* parcel_mgr.mParcelsPerEdge
//							/ 8;
//		U8* bitmap = new U8[ bitmap_size ];
//		msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
		msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, parcel_mgr.mCollisionBitmap, parcel_mgr.getCollisionBitmapSize());
// [/SL:KB]

		parcel_mgr.resetSegments(parcel_mgr.mCollisionSegments);
//		parcel_mgr.writeSegmentsFromBitmap( bitmap, parcel_mgr.mCollisionSegments );
// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
		parcel_mgr.writeSegmentsFromBitmap(parcel_mgr.mCollisionBitmap, parcel_mgr.mCollisionSegments);
// [/SL:KB]

//		delete[] bitmap;
//		bitmap = NULL;

// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
		LLViewerRegion* pRegion = LLWorld::getInstance()->getRegion(msg->getSender());
		parcel_mgr.mCollisionRegionHandle = (pRegion) ? pRegion->getHandle() : 0;

		if (parcel_mgr.mCollisionUpdateSignal)
			(*parcel_mgr.mCollisionUpdateSignal)(pRegion);
// [/SL:KB]
	}
	else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
	{
		LLViewerRegion *region = LLWorld::getInstance()->getRegion( msg->getSender() );
		if (region)
		{
			parcel_mgr.mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min );
			parcel_mgr.mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max );
		}
		else
		{
			parcel_mgr.mHoverWestSouth.clearVec();
			parcel_mgr.mHoverEastNorth.clearVec();
		}
	}
	else
	{
		// Check for video
		LLViewerParcelMedia::getInstance()->update(parcel);

		// Then check for music
		if (gAudiop)
		{
			if (parcel)
			{
                // Only update stream if parcel changed (recreated) or music is playing (enabled)
                static LLCachedControl<bool> already_playing(gSavedSettings, "MediaTentativeAutoPlay", true);
                if (!agent_parcel_update || already_playing)
                {
                    LLViewerParcelAskPlay::getInstance()->cancelNotification();
                    std::string music_url_raw = parcel->getMusicURL();

                    // Trim off whitespace from front and back
                    std::string music_url = music_url_raw;
                    LLStringUtil::trim(music_url);

                    // If there is a new music URL and it's valid, play it.
                    if (music_url.size() > 12)
                    {
                        if (music_url.substr(0, 7) == "http://"
                            || music_url.substr(0, 8) == "https://")
                        {
                            LLViewerRegion *region = LLWorld::getInstance()->getRegion(msg->getSender());
                            if (region)
                            {
                                optionallyStartMusic(music_url, parcel->mLocalID, region->getRegionID(), !agent_parcel_update);
                            }
                        }
                        else
                        {
                            LL_INFOS("ParcelMgr") << "Stopping parcel music (invalid audio stream URL)" << LL_ENDL;
                            // clears the URL
                            // null value causes fade out
                            LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLStringUtil::null);
                        }
                    }
                    else if (!gAudiop->getInternetStreamURL().empty())
                    {
                        LL_INFOS("ParcelMgr") << "Stopping parcel music (parcel stream URL is empty)" << LL_ENDL;
                        // null value causes fade out
                        LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLStringUtil::null);
                    }
                }
			}
			else
			{
				// Public land has no music
				LLViewerParcelAskPlay::getInstance()->cancelNotification();
				LLViewerAudio::getInstance()->stopInternetStreamWithAutoFade();
			}
		}//if gAudiop
	};

}

//static
void LLViewerParcelMgr::onStartMusicResponse(const LLUUID &region_id, const S32 &parcel_id, const std::string &url, const bool &play)
{
    if (play)
    {
        LL_INFOS("ParcelMgr") << "Starting parcel music " << url << LL_ENDL;
        LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(url);
    }
    else
    {
        LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLStringUtil::null);
    }
}

void LLViewerParcelMgr::optionallyStartMusic(const std::string &music_url, const S32 &local_id, const LLUUID &region_id, bool switched_parcel)
{
	static LLCachedControl<bool> streaming_music(gSavedSettings, "AudioStreamingMusic", true);
	if (streaming_music)
	{
		//static LLCachedControl<S32> autoplay_mode(gSavedSettings, "ParcelMediaAutoPlayEnable", 1); // <FS> Media/Stream separation
		static LLCachedControl<bool> tentative_autoplay(gSavedSettings, "MediaTentativeAutoPlay", true);
		// only play music when you enter a new parcel if the UI control for this
		// was not *explicitly* stopped by the user. (part of SL-4878)
        // <FS> Media/Stream separation
		//LLPanelNearByMedia* nearby_media_panel = gStatusBar->getNearbyMediaPanel();
        LLViewerAudio* viewer_audio = LLViewerAudio::getInstance();

        // ask mode //todo constants
        //if (autoplay_mode == 2)
        //{
        //    // if user set media to play - ask
        //    if ((nearby_media_panel && nearby_media_panel->getParcelAudioAutoStart())
        //        || (!nearby_media_panel && tentative_autoplay))
        //    {
        //        // user did not stop audio
        //        if (switched_parcel || music_url != viewer_audio->getNextStreamURI())
        //        {
        //            viewer_audio->startInternetStreamWithAutoFade(LLStringUtil::null);

        //            LLViewerParcelAskPlay::getInstance()->askToPlay(region_id,
        //                local_id,
        //                music_url,
        //                onStartMusicResponse);
        //        }
        //        // else do nothing:
        //        // Parcel properties changed, but not url.
        //        // We are already playing this url and asked about it when agent entered parcel
        //        // or user started audio manually at some point
        //    }
        //    else
        //    {
        //        // stopped by the user, do not autoplay
        //        LLViewerParcelAskPlay::getInstance()->cancelNotification();
        //        viewer_audio->startInternetStreamWithAutoFade(LLStringUtil::null);
        //    }
        //}
        // autoplay
        //else if ((nearby_media_panel
        //          && nearby_media_panel->getParcelAudioAutoStart())
        //          // or they have expressed no opinion in the UI, but have autoplay on...
        //         || (!nearby_media_panel
        //             && autoplay_mode == 1
        if (gStatusBar->getAudioStreamEnabled() ||
            // or they have expressed no opinion in the UI, but have autoplay on...
           (gSavedSettings.getBOOL("FSParcelMusicAutoPlay")
        // </FS>
                     && tentative_autoplay))
		{
			if (gSavedSettings.getBOOL("MediaEnableFilter"))
			{
				LLViewerParcelMedia::getInstance()->filterAudioUrl(music_url);
			}
			else
			{
				LL_INFOS("ParcelMgr") << "Starting parcel music " << music_url << LL_ENDL;
				viewer_audio->startInternetStreamWithAutoFade(music_url);
			}
		}
		// autoplay off
		else if(switched_parcel || music_url != viewer_audio->getNextStreamURI())
		{
			viewer_audio->startInternetStreamWithAutoFade(LLStringUtil::null);
		}
	}
}

// static
void LLViewerParcelMgr::processParcelAccessListReply(LLMessageSystem *msg, void **user)
{
	LLUUID agent_id;
	S32 sequence_id = 0;
	U32 message_flags = 0x0;
	S32 parcel_id = -1;

	msg->getUUIDFast(_PREHASH_Data, _PREHASH_AgentID, agent_id);
	msg->getS32Fast( _PREHASH_Data, _PREHASH_SequenceID, sequence_id );	//ignored
	msg->getU32Fast( _PREHASH_Data, _PREHASH_Flags, message_flags);
	msg->getS32Fast( _PREHASH_Data, _PREHASH_LocalID, parcel_id);

	LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
	if (!parcel) return;

	if (parcel_id != parcel->getLocalID())
	{
		LL_WARNS_ONCE("ParcelMgr") << "processParcelAccessListReply for parcel " << parcel_id
			<< " which isn't the selected parcel " << parcel->getLocalID()<< LL_ENDL;
		return;
	}

	if (message_flags & AL_ACCESS)
	{
		parcel->unpackAccessEntries(msg, &(parcel->mAccessList) );
	}
	else if (message_flags & AL_BAN)
	{
		parcel->unpackAccessEntries(msg, &(parcel->mBanList) );
	}
	else if (message_flags & AL_ALLOW_EXPERIENCE)
	{
		parcel->unpackExperienceEntries(msg, EXPERIENCE_KEY_TYPE_ALLOWED);
	}
	else if (message_flags & AL_BLOCK_EXPERIENCE)
	{
		parcel->unpackExperienceEntries(msg, EXPERIENCE_KEY_TYPE_BLOCKED);
	}
	/*else if (message_flags & AL_RENTER)
	{
		parcel->unpackAccessEntries(msg, &(parcel->mRenterList) );
	}*/

	LLViewerParcelMgr::getInstance()->notifyObservers();
}


// static
void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**)
{
	LLUUID agent_id;
	msg->getUUID("AgentData", "AgentID", agent_id);

	S32 local_id;
	msg->getS32("Data", "LocalID", local_id);

	LLUUID parcel_id;
	msg->getUUID("Data", "ParcelID", parcel_id);

	F32 dwell;
	msg->getF32("Data", "Dwell", dwell);

	if (local_id == LLViewerParcelMgr::getInstance()->mCurrentParcel->getLocalID())
	{
		LLViewerParcelMgr::getInstance()->mSelectedDwell = dwell;
		LLViewerParcelMgr::getInstance()->notifyObservers();
	}
}


void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
{
	if (!mSelected)
	{
		return;
	}

	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region) return;

	LLParcel* parcel = mCurrentParcel;
	if (!parcel) return;

	if (which & AL_ACCESS)
	{	
		sendParcelAccessListUpdate(AL_ACCESS, parcel->mAccessList, region, parcel->getLocalID());
	}

	if (which & AL_BAN)
	{	
		sendParcelAccessListUpdate(AL_BAN, parcel->mBanList, region, parcel->getLocalID());
	}

	if(which & AL_ALLOW_EXPERIENCE)
	{
		sendParcelAccessListUpdate(AL_ALLOW_EXPERIENCE, parcel->getExperienceKeysByType(EXPERIENCE_KEY_TYPE_ALLOWED), region, parcel->getLocalID());
	}
	if(which & AL_BLOCK_EXPERIENCE)
	{
		sendParcelAccessListUpdate(AL_BLOCK_EXPERIENCE, parcel->getExperienceKeysByType(EXPERIENCE_KEY_TYPE_BLOCKED), region, parcel->getLocalID());
	}
}

void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 flags, const LLAccessEntry::map& entries, LLViewerRegion* region, S32 parcel_local_id)
{
	S32 count = entries.size();
	S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
	S32 sequence_id = 1;
	BOOL start_message = TRUE;
	BOOL initial = TRUE;

	LLUUID transactionUUID;
	transactionUUID.generate();


	LLMessageSystem* msg = gMessageSystem;

	LLAccessEntry::map::const_iterator cit = entries.begin();
	LLAccessEntry::map::const_iterator  end = entries.end();
	while ( (cit != end) || initial ) 
	{
		if (start_message) 
		{
			msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
			msg->nextBlockFast(_PREHASH_AgentData);
			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
			msg->nextBlockFast(_PREHASH_Data);
			msg->addU32Fast(_PREHASH_Flags, flags);
			msg->addS32(_PREHASH_LocalID,  parcel_local_id);
			msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
			msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
			msg->addS32Fast(_PREHASH_Sections, num_sections);
			start_message = FALSE;

			if (initial && (cit == end))
			{
				// pack an empty block if there will be no data
				msg->nextBlockFast(_PREHASH_List);
				msg->addUUIDFast(_PREHASH_ID,  LLUUID::null );
				msg->addS32Fast(_PREHASH_Time, 0 );
				msg->addU32Fast(_PREHASH_Flags,	0 );
			}

			initial = FALSE;
			sequence_id++;

		}

		while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES)) 
		{
			const LLAccessEntry& entry = (*cit).second;

			msg->nextBlockFast(_PREHASH_List);
			msg->addUUIDFast(_PREHASH_ID,  entry.mID );
			msg->addS32Fast(_PREHASH_Time, entry.mTime );
			msg->addU32Fast(_PREHASH_Flags,	entry.mFlags );
			++cit;
		}

		start_message = TRUE;
		msg->sendReliable( region->getHost() );
	}
}


void LLViewerParcelMgr::deedLandToGroup()
{
	std::string group_name;
	gCacheName->getGroupName(mCurrentParcel->getGroupID(), group_name);
	LLSD args;
	args["AREA"] = llformat("%d", mCurrentParcel->getArea());
	args["GROUP_NAME"] = group_name;
	if(mCurrentParcel->getContributeWithDeed())
	{
		args["NAME"] = LLSLURL("agent", mCurrentParcel->getOwnerID(), "completename").getSLURLString();
		LLNotificationsUtil::add("DeedLandToGroupWithContribution",args, LLSD(), deedAlertCB);
	}
	else
	{
		LLNotificationsUtil::add("DeedLandToGroup",args, LLSD(), deedAlertCB);
	}
}

// static
bool LLViewerParcelMgr::deedAlertCB(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	if (option == 0)
	{
		LLParcel* parcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
		LLUUID group_id;
		if(parcel)
		{
			group_id = parcel->getGroupID();
		}
		LLViewerParcelMgr::getInstance()->sendParcelDeed(group_id);
	}
	return false;
}


void LLViewerParcelMgr::startReleaseLand()
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotReleaseLandNothingSelected");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_NO_DATA)
	{
		LLNotificationsUtil::add("CannotReleaseLandWatingForServer");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_MULTIPLE)
	{
		LLNotificationsUtil::add("CannotReleaseLandSelected");
		return;
	}

	if (!isParcelOwnedByAgent(mCurrentParcel, GP_LAND_RELEASE)
		&& !(gAgent.canManageEstate()))
	{
		LLNotificationsUtil::add("CannotReleaseLandDontOwn");
		return;
	}

	LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
	if (!region)
	{
		LLNotificationsUtil::add("CannotReleaseLandRegionNotFound");
		return;
	}
/*
	if (region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL)
		&& !gAgent.isGodlike())
	{
		LLSD args;
		args["REGION"] = region->getName();
		LLNotificationsUtil::add("CannotReleaseLandNoTransfer", args);
		return;
	}
*/

	if (!mCurrentParcelSelection->mWholeParcelSelected)
	{
		LLNotificationsUtil::add("CannotReleaseLandPartialSelection");
		return;
	}

	// Compute claim price
	LLSD args;
	args["AREA"] = llformat("%d",mCurrentParcel->getArea());
	LLNotificationsUtil::add("ReleaseLandWarning", args, LLSD(), releaseAlertCB);
}

bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const
{
	if (!parcel)
	{
		return false;
	}
	
	if (mSelected  &&  parcel == mCurrentParcel)
	{
		if (mRequestResult == PARCEL_RESULT_NO_DATA)
		{
			return false;
		}
	}
	
	const LLUUID& parcelOwner = parcel->getOwnerID();
	const LLUUID& authorizeBuyer = parcel->getAuthorizedBuyerID();

	if (parcel->isPublic())
	{
		return true;	// change this if want to make it gods only
	}
	
	LLVector3 parcel_coord = parcel->getCenterpoint();
	LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosAgent(parcel_coord);
	if (regionp)
	{
		U8 sim_access = regionp->getSimAccess();
		const LLAgentAccess& agent_access = gAgent.getAgentAccess();
		// if the region is PG, we're happy already, so do nothing
		// but if we're set to avoid either mature or adult, get us outta here
		if ((sim_access == SIM_ACCESS_MATURE) &&
			!agent_access.canAccessMature())
		{
			return false;
		}
		else if ((sim_access == SIM_ACCESS_ADULT) &&
				 !agent_access.canAccessAdult())
		{
			return false;
		}
	}	
	
	bool isForSale = parcel->getForSale()
			&& ((parcel->getSalePrice() > 0) || (authorizeBuyer.notNull()));
			
	bool isEmpowered
		= forGroup ? gAgent.hasPowerInActiveGroup(GP_LAND_DEED) == TRUE : true;
		
	bool isOwner
		= parcelOwner == (forGroup ? gAgent.getGroupID() : gAgent.getID());
	
	bool isAuthorized
			= (authorizeBuyer.isNull()
				|| (gAgent.getID() == authorizeBuyer)
				|| (gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_DEED)
					&& gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_SET_SALE_INFO)));
	
	return isForSale && !isOwner && isAuthorized  && isEmpowered;
}


void LLViewerParcelMgr::startBuyLand(BOOL is_for_group)
{
	LLFloaterBuyLand::buyLand(getSelectionRegion(), mCurrentParcelSelection, is_for_group == TRUE);
}

void LLViewerParcelMgr::startSellLand()
{
	LLFloaterSellLand::sellLand(getSelectionRegion(), mCurrentParcelSelection);
}

void LLViewerParcelMgr::startDivideLand()
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotDivideLandNothingSelected");
		return;
	}

	if (mCurrentParcelSelection->mWholeParcelSelected)
	{
		LLNotificationsUtil::add("CannotDivideLandPartialSelection");
		return;
	}

	LLSD payload;
	payload["west_south_border"] = ll_sd_from_vector3d(mWestSouth);
	payload["east_north_border"] = ll_sd_from_vector3d(mEastNorth);

	LLNotificationsUtil::add("LandDivideWarning", LLSD(), payload, callbackDivideLand);
}

// static
bool LLViewerParcelMgr::callbackDivideLand(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	LLVector3d west_south_d = ll_vector3d_from_sd(notification["payload"]["west_south_border"]);
	LLVector3d east_north_d = ll_vector3d_from_sd(notification["payload"]["east_north_border"]);
	LLVector3d parcel_center = (west_south_d + east_north_d) / 2.0;

	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
	if (!region)
	{
		LLNotificationsUtil::add("CannotDivideLandNoRegion");
		return false;
	}

	if (0 == option)
	{
		LLVector3 west_south = region->getPosRegionFromGlobal(west_south_d);
		LLVector3 east_north = region->getPosRegionFromGlobal(east_north_d);

		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("ParcelDivide");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->nextBlock("ParcelData");
		msg->addF32("West", west_south.mV[VX]);
		msg->addF32("South", west_south.mV[VY]);
		msg->addF32("East", east_north.mV[VX]);
		msg->addF32("North", east_north.mV[VY]);
		msg->sendReliable(region->getHost());
	}
	return false;
}


void LLViewerParcelMgr::startJoinLand()
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotJoinLandNothingSelected");
		return;
	}

	if (mCurrentParcelSelection->mWholeParcelSelected)
	{
		LLNotificationsUtil::add("CannotJoinLandEntireParcelSelected");
		return;
	}

	if (!mCurrentParcelSelection->mSelectedMultipleOwners)
	{
		LLNotificationsUtil::add("CannotJoinLandSelection");
		return;
	}

	LLSD payload;
	payload["west_south_border"] = ll_sd_from_vector3d(mWestSouth);
	payload["east_north_border"] = ll_sd_from_vector3d(mEastNorth);

	LLNotificationsUtil::add("JoinLandWarning", LLSD(), payload, callbackJoinLand);
}

// static
bool LLViewerParcelMgr::callbackJoinLand(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	LLVector3d west_south_d = ll_vector3d_from_sd(notification["payload"]["west_south_border"]);
	LLVector3d east_north_d = ll_vector3d_from_sd(notification["payload"]["east_north_border"]);
	LLVector3d parcel_center = (west_south_d + east_north_d) / 2.0;

	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
	if (!region)
	{
		LLNotificationsUtil::add("CannotJoinLandNoRegion");
		return false;
	}

	if (0 == option)
	{
		LLVector3 west_south = region->getPosRegionFromGlobal(west_south_d);
		LLVector3 east_north = region->getPosRegionFromGlobal(east_north_d);

		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("ParcelJoin");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->nextBlock("ParcelData");
		msg->addF32("West", west_south.mV[VX]);
		msg->addF32("South", west_south.mV[VY]);
		msg->addF32("East", east_north.mV[VX]);
		msg->addF32("North", east_north.mV[VY]);
		msg->sendReliable(region->getHost());
	}
	return false;
}


void LLViewerParcelMgr::startDeedLandToGroup()
{
	if (!mSelected || !mCurrentParcel)
	{
		LLNotificationsUtil::add("CannotDeedLandNothingSelected");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_NO_DATA)
	{
		LLNotificationsUtil::add("CannotDeedLandWaitingForServer");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_MULTIPLE)
	{
		LLNotificationsUtil::add("CannotDeedLandMultipleSelected");
		return;
	}

	LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
	if (!region)
	{
		LLNotificationsUtil::add("CannotDeedLandNoRegion");
		return;
	}

	/*
	if(!gAgent.isGodlike())
	{
		if(region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL)
			&& (mCurrentParcel->getOwnerID() != region->getOwner()))
		{
			LLSD args;
			args["REGION"] = region->getName();
			LLNotificationsUtil::add("CannotDeedLandNoTransfer", args);
			return;
		}
	}
	*/

	deedLandToGroup();
}
void LLViewerParcelMgr::reclaimParcel()
{
	LLParcel* parcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
	LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
	if(parcel && parcel->getOwnerID().notNull()
	   && (parcel->getOwnerID() != gAgent.getID())
	   && regionp && (regionp->getOwner() == gAgent.getID()))
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessage("ParcelReclaim");
		msg->nextBlock("AgentData");
		msg->addUUID("AgentID", gAgent.getID());
		msg->addUUID("SessionID", gAgent.getSessionID());
		msg->nextBlock("Data");
		msg->addS32("LocalID", parcel->getLocalID());
		msg->sendReliable(regionp->getHost());
	}
}

// static
bool LLViewerParcelMgr::releaseAlertCB(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	if (option == 0)
	{
		// Send the release message, not a force
		LLViewerParcelMgr::getInstance()->sendParcelRelease();
	}
	return false;
}

void LLViewerParcelMgr::buyPass()
{
	LLParcel* parcel = getParcelSelection()->getParcel();
	if (!parcel) return;

	LLViewerRegion* region = getSelectionRegion();
	if (!region) return;

	LLMessageSystem* msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ParcelBuyPass);
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID());
	msg->addUUID("SessionID", gAgent.getSessionID());
	msg->nextBlockFast(_PREHASH_ParcelData);
	msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
	msg->sendReliable( region->getHost() );
}

//Tells whether we are allowed to buy a pass or not
BOOL LLViewerParcelMgr::isCollisionBanned()	
{ 
	if ((mCollisionBanned == BA_ALLOWED) || (mCollisionBanned == BA_NOT_ON_LIST) || (mCollisionBanned == BA_NOT_IN_GROUP))
		return FALSE;
	else 
		return TRUE;
}

// This implementation should mirror LLSimParcelMgr::isParcelOwnedBy
// static
BOOL LLViewerParcelMgr::isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power)
{
	if (!parcelp)
	{
		return FALSE;
	}

	// Gods can always assume ownership.
	if (gAgent.isGodlike())
	{
		return TRUE;
	}

	// The owner of a parcel automatically gets all powersr.
	if (parcelp->getOwnerID() == gAgent.getID())
	{
		return TRUE;
	}

	// Only gods can assume 'ownership' of public land.
	if (parcelp->isPublic())
	{
		return FALSE;
	}

	// Return whether or not the agent has group_proxy_power powers in the
	// parcel's group.
	return gAgent.hasPowerInGroup(parcelp->getOwnerID(), group_proxy_power);
}

// This implementation should mirror llSimParcelMgr::isParcelModifiableBy
// static
BOOL LLViewerParcelMgr::isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power)
{
	// If the agent can assume ownership, it is probably modifiable.
	BOOL rv = FALSE;
	if (parcelp)
	{
		// *NOTE: This should only work for leased parcels, but group owned
		// parcels cannot be OS_LEASED yet. Phoenix 2003-12-15.
		rv = isParcelOwnedByAgent(parcelp, group_proxy_power);

		// ... except for the case that the parcel is not OS_LEASED for agent-owned parcels.
		if( (gAgent.getID() == parcelp->getOwnerID())
			&& !gAgent.isGodlike()
			&& (parcelp->getOwnershipStatus() != LLParcel::OS_LEASED) )
		{
			rv = FALSE;
		}
	}
	return rv;
}

void sanitize_corners(const LLVector3d &corner1,
										const LLVector3d &corner2,
										LLVector3d &west_south_bottom,
										LLVector3d &east_north_top)
{
	west_south_bottom.mdV[VX] = llmin( corner1.mdV[VX], corner2.mdV[VX] );
	west_south_bottom.mdV[VY] = llmin( corner1.mdV[VY], corner2.mdV[VY] );
	west_south_bottom.mdV[VZ] = llmin( corner1.mdV[VZ], corner2.mdV[VZ] );

	east_north_top.mdV[VX] = llmax( corner1.mdV[VX], corner2.mdV[VX] );
	east_north_top.mdV[VY] = llmax( corner1.mdV[VY], corner2.mdV[VY] );
	east_north_top.mdV[VZ] = llmax( corner1.mdV[VZ], corner2.mdV[VZ] );
}


void LLViewerParcelMgr::cleanupGlobals()
{
}

LLViewerTexture* LLViewerParcelMgr::getBlockedImage() const
{
	return sBlockedImage;
}

LLViewerTexture* LLViewerParcelMgr::getPassImage() const
{
	return sPassImage;
}

/*
 * Set finish teleport callback. You can use it to observe all  teleport events.
 * NOTE:
 * After local( in one region) teleports we
 *  cannot rely on gAgent.getPositionGlobal(),
 *  so the new position gets passed explicitly.
 *  Use args of this callback to get global position of avatar after teleport event.
 */
boost::signals2::connection LLViewerParcelMgr::setTeleportFinishedCallback(teleport_finished_callback_t cb)
{
	return mTeleportFinishedSignal.connect(cb);
}

boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(teleport_failed_callback_t cb)
{
	return mTeleportFailedSignal.connect(cb);
}

/* Ok, we're notified that teleport has been finished.
 * We should now propagate the notification via mTeleportFinishedSignal
 * to all interested parties.
 */
void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos)
{
	// Treat only teleports within the same parcel as local (EXT-3139).
	if (local && LLViewerParcelMgr::getInstance()->inAgentParcel(new_pos))
	{
		// Local teleport. We already have the agent parcel data.
		// Emit the signal immediately.
		getInstance()->mTeleportFinishedSignal(new_pos, local);
	}
	else
	{
		// Non-local teleport (inter-region or between different parcels of the same region).
		// The agent parcel data has not been updated yet.
		// Let's wait for the update and then emit the signal.
		mTeleportInProgressPosition = new_pos;
		mTeleportInProgress = TRUE;
	}
}

void LLViewerParcelMgr::onTeleportFailed()
{
	mTeleportFailedSignal();
}

bool  LLViewerParcelMgr::getTeleportInProgress()
{
    return mTeleportInProgress // case where parcel data arrives after teleport
        || gAgent.getTeleportState() > LLAgent::TELEPORT_NONE; // For LOCAL, no mTeleportInProgress
}

// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
boost::signals2::connection LLViewerParcelMgr::setCollisionUpdateCallback(const collision_update_signal_t::slot_type& cb)
{
	if (!mCollisionUpdateSignal)
		mCollisionUpdateSignal = new collision_update_signal_t();
	return mCollisionUpdateSignal->connect(cb); 
}
// [/SL:KB]
