/** 
 * @file llviewerparcelmgr.cpp
 * @brief Viewer-side representation of owned land
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llviewerparcelmgr.h"

// Library includes
#include "llaudioengine.h"
#include "indra_constants.h"
#include "llcachename.h"
#include "llgl.h"
#include "llparcel.h"
#include "llsecondlifeurls.h"
#include "message.h"

// Viewer includes
#include "llagent.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
#include "llfirstuse.h"
#include "llfloaterbuyland.h"
#include "llfloatergroups.h"
#include "llfloatersellland.h"
#include "llfloatertools.h"
#include "llnotify.h"
#include "llparcelselection.h"
#include "llresmgr.h"
#include "llsdutil.h"
#include "llsdutil_math.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 "lloverlaybar.h"
#include "roles_constants.h"
#include "llweb.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 optionally_start_music(const std::string& music_url);
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(0.f),
	mAgentParcelSequenceID(-1),
	mHoverRequestResult(0),
	mHoverWestSouth(),
	mHoverEastNorth(),
	mRenderCollision(FALSE),
	mRenderSelection(TRUE),
	mCollisionBanned(0),
	mCollisionTimer(),
	mMediaParcelId(0),
	mMediaRegionId(0)
{
	mCurrentParcel = new LLParcel();
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
	mFloatingParcelSelection = new LLParcelSelection(mCurrentParcel);

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

	mParcelsPerEdge = S32(	REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
	mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
	resetSegments(mHighlightSegments);

	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");
	mPassImage = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png");

	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;
	}

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


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;

	delete[] mCollisionSegments;
	mCollisionSegments = NULL;

	delete[] sPackedOverlay;
	sPackedOverlay = NULL;

	delete[] mAgentParcelOverlay;
	mAgentParcelOverlay = NULL;

	sBlockedImage = NULL;
	sPassImage = NULL;
}

void LLViewerParcelMgr::dump()
{
	llinfos << "Parcel Manager Dump" << llendl;
	llinfos << "mSelected " << S32(mSelected) << llendl;
	llinfos << "Selected parcel: " << llendl;
	llinfos << mWestSouth << " to " << mEastNorth << llendl;
	mCurrentParcel->dump();
	llinfos << "banning " << mCurrentParcel->mBanList.size() << llendl;
	
	access_map_const_iterator cit = mCurrentParcel->mBanList.begin();
	access_map_const_iterator end = mCurrentParcel->mBanList.end();
	for ( ; cit != end; ++cit)
	{
		llinfos << "ban id " << (*cit).first << llendl;
	}
	llinfos << "Hover parcel:" << llendl;
	mHoverParcel->dump();
	llinfos << "Agent parcel:" << llendl;
	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 = 0.f;

	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 = llround(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 = llround( west / PARCEL_GRID_STEP_METERS );
	S32 max_x = llround( east / PARCEL_GRID_STEP_METERS );
	S32 min_y = llround( south / PARCEL_GRID_STEP_METERS );
	S32 max_y = llround( 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] = llround( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	southwest.mdV[VY] = llround( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );

	northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
	northeast.mdV[VX] = llround( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	northeast.mdV[VY] = llround( 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
	mWestSouth = gAgent.getRegion()->getOriginGlobal();
	mEastNorth = mWestSouth;
	mEastNorth += LLVector3d(PARCEL_GRID_STEP_METERS, PARCEL_GRID_STEP_METERS, 0.0);

	// 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)
	{
		LLNotifications::instance().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;

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

	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 = 0.f;

		// 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.put(observer);
}


void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
{
	mObservers.removeObj(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()
{
	LLDynamicArray<LLParcelObserver*> observers;
	S32 count = mObservers.count();
	S32 i;
	for(i = 0; i < count; ++i)
	{
		observers.put(mObservers.get(i));
	}
	for(i = 0; i < count; ++i)
	{
		observers.get(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;
}

// Return whether the agent can build on the land they are on
bool LLViewerParcelMgr::agentCanBuild() const
{
	if (mAgentParcel)
	{
		return (gAgent.isGodlike() ||
				(mAgentParcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID())) ||
				(isParcelOwnedByAgent(mAgentParcel, GP_LAND_ALLOW_CREATE)));
	}
	else
	{
		return gAgent.isGodlike();
	}
}

BOOL LLViewerParcelMgr::agentCanTakeDamage() const
{
	return mAgentParcel->getAllowDamage();
}

BOOL LLViewerParcelMgr::agentCanFly() const
{
	return TRUE;
}

F32 LLViewerParcelMgr::agentDrawDistance() const
{
	return 512.f;
}

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)
	{
		// 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();
		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;

	LLMessageSystem *msg = gMessageSystem;
	

	if (flags & AL_BAN) 
	{
		mCurrentParcel->mBanList.clear();
	}
	if (flags & AL_ACCESS) 
	{
		mCurrentParcel->mAccessList.clear();
	}		

	// 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)
	{
		LLNotifications::instance().add("CannotSetLandOwnerNothingSelected");
		return;
	}

	llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;

	// 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.
		LLNotifications::instance().add("CannotContentifyNoRegion");
		return;
	}

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

	llinfos << "Region " << region->getOriginGlobal() << llendl;

	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 = LLNotification::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)
	{
		LLNotifications::instance().add("CannotContentifyNothingSelected");
		return;
	}
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotifications::instance().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)
	{
        LLNotifications::instance().add("CannotReleaseLandNothingSelected");
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotifications::instance().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)
	{
		LLNotifications::instance().add("CannotBuyLandNothingSelected");
		return NULL;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotifications::instance().add("CannotBuyLandNoRegion");
		return NULL;
	}
	
	if (is_claim)
	{
		llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
		llinfos << "Region " << region->getOriginGlobal() << llendl;

		// 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)
		{
			LLNotifications::instance().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)
{
	delete info;
	info = NULL;
}

void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id)
{
	if (!mSelected || !mCurrentParcel)
	{
		LLNotifications::instance().add("CannotDeedLandNothingSelected");
		return;
	}
	if(group_id.isNull())
	{
		LLNotifications::instance().add("CannotDeedLandNoGroup");
		return;
	}
	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
	if (!region)
	{
		LLNotifications::instance().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();
}


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;
	//llinfos << "found region: " << region->getName() << llendl;

	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);
		llinfos << "Sending parcel properties update via capability to: "
			<< url << llendl;
		LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
	}
	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)
{
	//FIXME: only request parcel info when tooltip is shown
	return;
	/*LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos );
	if (!region)
	{
		return;
	}

	// 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
	LLVector3 wsb_region = region->getPosRegionFromGlobal( pos );

	F32 west  = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VX] / PARCEL_GRID_STEP_METERS );
	F32 south = PARCEL_GRID_STEP_METERS * floor( wsb_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)
{
	if (gNoRender)
	{
		return;
	}

	// Extract the packed overlay information
	S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data);

	if (packed_overlay_size <= 0)
	{
		llwarns << "Overlay size " << packed_overlay_size << llendl;
		return;
	}

	S32 parcels_per_edge = LLViewerParcelMgr::getInstance()->mParcelsPerEdge;
	S32 expected_size = parcels_per_edge * parcels_per_edge / PARCEL_OVERLAY_CHUNKS;
	if (packed_overlay_size != expected_size)
	{
		llwarns << "Got parcel overlay size " << packed_overlay_size
			<< " expecting " << expected_size << llendl;
		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;

	S32		other_clean_time = 0;

	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
		llinfos << "no valid parcel data" << llendl;
		return;
	}

	// Decide where the data will go.
	LLParcel* parcel = NULL;
	if (sequence_id == SELECTED_PARCEL_SEQ_ID)
	{
		// ...selected parcels report this sequence id
		LLViewerParcelMgr::getInstance()->mRequestResult = PARCEL_RESULT_SUCCESS;
		parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
	}
	else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
	{
		LLViewerParcelMgr::getInstance()->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
		parcel = LLViewerParcelMgr::getInstance()->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)
	{
		LLViewerParcelMgr::getInstance()->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
		parcel = LLViewerParcelMgr::getInstance()->mCollisionParcel;
	}
	else if (sequence_id == 0 || sequence_id > LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID)
	{
		// new agent parcel
		LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID = sequence_id;
		parcel = LLViewerParcelMgr::getInstance()->mAgentParcel;
	}
	else
	{
		llinfos << "out of order agent parcel sequence id " << sequence_id
			<< " last good " << LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID
			<< llendl;
		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 );
	}

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

	// Actually extract the data.
	if (parcel)
	{
		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->unpackMessage(msg);

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

			LLViewerParcelMgr::getInstance()->writeAgentParcelFromBitmap(bitmap);
			delete[] bitmap;

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

			instance->mAgentParcelChangedSignal();

			if (instance->mTeleportInProgress)
			{
				instance->mTeleportInProgress = FALSE;
				instance->mTeleportFinishedSignal(gAgent.getPositionGlobal());
			}
		}
	}

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

		LLViewerParcelMgr::getInstance()->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(LLViewerParcelMgr::getInstance()->mWestSouth);
				LLVector3 east_north = region->getPosRegionFromGlobal(LLViewerParcelMgr::getInstance()->mEastNorth);

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

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

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

				LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mHighlightSegments);
				LLViewerParcelMgr::getInstance()->writeSegmentsFromBitmap( bitmap, LLViewerParcelMgr::getInstance()->mHighlightSegments );

				delete[] bitmap;
				bitmap = NULL;

				LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mWholeParcelSelected = TRUE;
			}

			// Request access list information for this land
			LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN);

			// Request the media url filter list for this land
			LLViewerParcelMgr::getInstance()->requestParcelMediaURLFilter();

			// Request dwell for this land, if it's not public land.
			LLViewerParcelMgr::getInstance()->mSelectedDwell = 0.f;
			if (0 != local_id)
			{
				LLViewerParcelMgr::getInstance()->sendParcelDwellRequest();
			}

			LLViewerParcelMgr::getInstance()->mSelected = TRUE;
			LLViewerParcelMgr::getInstance()->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
		LLViewerParcelMgr::getInstance()->mRenderCollision = TRUE;
		LLViewerParcelMgr::getInstance()->mCollisionTimer.reset();

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

		}

		S32 bitmap_size =	LLViewerParcelMgr::getInstance()->mParcelsPerEdge
							* LLViewerParcelMgr::getInstance()->mParcelsPerEdge
							/ 8;
		U8* bitmap = new U8[ bitmap_size ];
		msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);

		LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mCollisionSegments);
		LLViewerParcelMgr::getInstance()->writeSegmentsFromBitmap( bitmap, LLViewerParcelMgr::getInstance()->mCollisionSegments );

		delete[] bitmap;
		bitmap = NULL;

	}
	else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
	{
		LLViewerRegion *region = LLWorld::getInstance()->getRegion( msg->getSender() );
		if (region)
		{
			LLViewerParcelMgr::getInstance()->mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min );
			LLViewerParcelMgr::getInstance()->mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max );
		}
		else
		{
			LLViewerParcelMgr::getInstance()->mHoverWestSouth.clearVec();
			LLViewerParcelMgr::getInstance()->mHoverEastNorth.clearVec();
		}
	}
	else
	{
		// look for music.
		if (gAudiop)
		{
			if (parcel)
			{
				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);

				// On entering a new parcel, stop the last stream if the
				// new parcel has a different music url.  (Empty URL counts
				// as different.)
				const std::string& stream_url = gAudiop->getInternetStreamURL();

				if (music_url.empty() || music_url != stream_url)
				{
					// URL is different from one currently playing.
					gAudiop->stopInternetStream();

					// 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://")
						{
							optionally_start_music(music_url);
						}
					}
					else if (!gAudiop->getInternetStreamURL().empty())
					{
						llinfos << "Stopping parcel music" << llendl;
						gAudiop->startInternetStream(LLStringUtil::null);
					}
				}
			}
			else
			{
				// Public land has no music
				gAudiop->stopInternetStream();
			}
		}//if gAudiop

		// now check for video
		LLViewerParcelMedia::update( parcel );
	};
}

void optionally_start_music(const std::string& music_url)
{
	if (gSavedSettings.getBOOL("AudioStreamingMusic") && gSavedSettings.getBOOL("AudioSteamingMedia"))
	{
		// Make the user click the start button on the overlay bar. JC
		//		llinfos << "Starting parcel music " << music_url << llendl;

		// now only play music when you enter a new parcel if the control is in PLAY state
		// changed as part of SL-4878
		if ( gOverlayBar && gOverlayBar->musicPlaying())
		{
			gAudiop->startInternetStream(music_url);
		}
	}
}

// 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())
	{
		llwarns << "processParcelAccessListReply for parcel " << parcel_id
			<< " which isn't the selected parcel " << parcel->getLocalID()<< llendl;
		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_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)
{

	LLUUID transactionUUID;
	transactionUUID.generate();

	if (!mSelected)
	{
		return;
	}

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

	LLMessageSystem* msg = gMessageSystem;

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

	if (which & AL_ACCESS)
	{	
		S32 count = parcel->mAccessList.size();
		S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
		S32 sequence_id = 1;
		BOOL start_message = TRUE;
		BOOL initial = TRUE;

		access_map_const_iterator cit = parcel->mAccessList.begin();
		access_map_const_iterator end = parcel->mAccessList.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, AL_ACCESS);
				msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
				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() );
		}
	}

	if (which & AL_BAN)
	{	
		S32 count = parcel->mBanList.size();
		S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
		S32 sequence_id = 1;
		BOOL start_message = TRUE;
		BOOL initial = TRUE;

		access_map_const_iterator cit = parcel->mBanList.begin();
		access_map_const_iterator end = parcel->mBanList.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, AL_BAN);
				msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
				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() );
		}
	}
}

class LLParcelMediaURLFilterResponder : public LLHTTPClient::Responder
{
	virtual void result(const LLSD& content)
	{
		LLViewerParcelMgr::getInstance()->receiveParcelMediaURLFilter(content);
	}
};

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

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

	LLParcel* parcel = mCurrentParcel;
	if (!parcel)
	{
		llwarns << "no parcel" << llendl;
		return;
	}

	LLSD body;
	body["local-id"] = parcel->getLocalID();
	body["list"] = parcel->getMediaURLFilterList();

	std::string url = region->getCapability("ParcelMediaURLFilterList");
	if (!url.empty())
	{
		LLHTTPClient::post(url, body, new LLParcelMediaURLFilterResponder);
	}
	else
	{
		llwarns << "can't get ParcelMediaURLFilterList cap" << llendl;
	}
}


void LLViewerParcelMgr::receiveParcelMediaURLFilter(const LLSD &content)
{
	if (content.has("list"))
	{
		LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
		if (!parcel) return;
		
		if (content["local-id"].asInteger() == parcel->getLocalID())
		{
			parcel->setMediaURLFilterList(content["list"]);
			
			LLViewerParcelMgr::getInstance()->notifyObservers();
		}
	}
}


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())
	{
		std::string first_name, last_name;
		gCacheName->getName(mCurrentParcel->getOwnerID(), first_name, last_name);
		args["FIRST_NAME"] = first_name;
		args["LAST_NAME"] = last_name;
		LLNotifications::instance().add("DeedLandToGroupWithContribution",args, LLSD(), deedAlertCB);
	}
	else
	{
		LLNotifications::instance().add("DeedLandToGroup",args, LLSD(), deedAlertCB);
	}
}

// static
bool LLViewerParcelMgr::deedAlertCB(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::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)
	{
		LLNotifications::instance().add("CannotReleaseLandNothingSelected");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_NO_DATA)
	{
		LLNotifications::instance().add("CannotReleaseLandWatingForServer");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_MULTIPLE)
	{
		LLNotifications::instance().add("CannotReleaseLandSelected");
		return;
	}

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

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

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

	// Compute claim price
	LLSD args;
	args["AREA"] = llformat("%d",mCurrentParcel->getArea());
	LLNotifications::instance().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
	}
	
	LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
	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));
	
	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)
	{
		LLNotifications::instance().add("CannotDivideLandNothingSelected");
		return;
	}

	if (mCurrentParcelSelection->mWholeParcelSelected)
	{
		LLNotifications::instance().add("CannotDivideLandPartialSelection");
		return;
	}

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

	LLNotifications::instance().add("LandDivideWarning", LLSD(), payload, callbackDivideLand);
}

// static
bool LLViewerParcelMgr::callbackDivideLand(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::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)
	{
		LLNotifications::instance().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)
	{
		LLNotifications::instance().add("CannotJoinLandNothingSelected");
		return;
	}

	if (mCurrentParcelSelection->mWholeParcelSelected)
	{
		LLNotifications::instance().add("CannotJoinLandEntireParcelSelected");
		return;
	}

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

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

	LLNotifications::instance().add("JoinLandWarning", LLSD(), payload, callbackJoinLand);
}

// static
bool LLViewerParcelMgr::callbackJoinLand(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::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)
	{
		LLNotifications::instance().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)
	{
		LLNotifications::instance().add("CannotDeedLandNothingSelected");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_NO_DATA)
	{
		LLNotifications::instance().add("CannotDeedLandWaitingForServer");
		return;
	}

	if (mRequestResult == PARCEL_RESULT_MULTIPLE)
	{
		LLNotifications::instance().add("CannotDeedLandMultipleSelected");
		return;
	}

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

	/*
	if(!gAgent.isGodlike())
	{
		if((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
			&& (mCurrentParcel->getOwnerID() != region->getOwner()))
		{
			LLSD args;
			args["REGION"] = region->getName();
			LLNotifications::instance().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 = LLNotification::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()
{
	LLParcelSelection::sNullSelection = NULL;
}

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

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

boost::signals2::connection LLViewerParcelMgr::addAgentParcelChangedCallback(parcel_changed_callback_t cb)
{
	return mAgentParcelChangedSignal.connect(cb);
}
/*
 * 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(parcel_changed_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)
{
	if (local)
	{
		// Local teleport. We already have the agent parcel data.
		// Emit the signal immediately.
		getInstance()->mTeleportFinishedSignal(new_pos);
	}
	else
	{
		// Non-local teleport.
		// The agent parcel data has not been updated yet.
		// Let's wait for the update and then emit the signal.
		mTeleportInProgress = TRUE;
	}
}

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