/** 
* @file lllandmarkactions.cpp
* @brief LLLandmarkActions class implementation
*
* $LicenseInfo:firstyear=2001&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 "lllandmarkactions.h"

#include "roles_constants.h"

// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2012-08-01 (Catznip-3.3)
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
// [/SL:KB]
#include "llinventory.h"
#include "llinventoryfunctions.h"
#include "lllandmark.h"
#include "llparcel.h"
#include "llregionhandle.h"

#include "llnotificationsutil.h"

#include "llagent.h"
#include "llagentui.h"
#include "llinventorymodel.h"
#include "lllandmarklist.h"
#include "llslurl.h"
#include "llstring.h"
// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2012-08-01 (Catznip-3.3)
#include "llviewercontrol.h"
// [/SL:KB]
#include "llviewerinventory.h"
#include "llviewerparcelmgr.h"
#include "llworldmapmessage.h"
#include "llviewerwindow.h"
#include "llwindow.h"
#include "llworldmap.h"

// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
#define MAX_PENDING_LANDMARK_REQUEST      250
// [/SL:KB]

void copy_slurl_to_clipboard_callback(const std::string& slurl);

class LLFetchlLandmarkByPos : public LLInventoryCollectFunctor
{
private:
	LLVector3d mPos;
public:
	LLFetchlLandmarkByPos(const LLVector3d& pos) :
		mPos(pos)
	{}

	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		LLVector3d landmark_global_pos;
		if (!landmark->getGlobalPos(landmark_global_pos))
			return false;
		//we have to round off each coordinates to compare positions properly
		return ll_round(mPos.mdV[VX]) ==  ll_round(landmark_global_pos.mdV[VX])
				&& ll_round(mPos.mdV[VY]) ==  ll_round(landmark_global_pos.mdV[VY])
				&& ll_round(mPos.mdV[VZ]) ==  ll_round(landmark_global_pos.mdV[VZ]);
	}
};

class LLFetchLandmarksByName : public LLInventoryCollectFunctor
{
private:
	std::string name;
	BOOL use_substring;
	//this member will be contain copy of founded items to keep the result unique
	std::set<std::string> check_duplicate;

public:
LLFetchLandmarksByName(std::string &landmark_name, BOOL if_use_substring)
:name(landmark_name),
use_substring(if_use_substring)
	{
	LLStringUtil::toLower(name);
	}

public:
	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		bool acceptable = false;
		std::string landmark_name = item->getName();
		LLStringUtil::toLower(landmark_name);
		if(use_substring)
		{
			acceptable =  landmark_name.find( name ) != std::string::npos;
		}
		else
		{
			acceptable = landmark_name == name;
		}
		if(acceptable){
			if(check_duplicate.find(landmark_name) != check_duplicate.end()){
				// we have duplicated items in landmarks
				acceptable = false;
			}else{
				check_duplicate.insert(landmark_name);
			}
		}

		return acceptable;
	}
};

// Returns true if the given inventory item is a landmark pointing to the current parcel.
// Used to find out if there is at least one landmark from current parcel.
class LLFirstAgentParcelLandmark : public LLInventoryCollectFunctor
{
private:	
	bool mFounded;// to avoid unnecessary  check
// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
	bool mRequestUnloaded = true;
// [/SL:KB]
	
public:
//	LLFirstAgentParcelLandmark(): mFounded(false){}
// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
	LLFirstAgentParcelLandmark(bool request_unloaded = true)
		: mFounded(false)
		, mRequestUnloaded(request_unloaded)
	{
	}
// [/SL:KB]

	
	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (mFounded || !item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
		if ( (mRequestUnloaded && gLandmarkList.getRequestedCount() > MAX_PENDING_LANDMARK_REQUEST) ||
			 (!mRequestUnloaded && !gLandmarkList.assetExists(item->getAssetUUID())) )
		{
			return false;
		}
// [/SL:KB]

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		LLVector3d landmark_global_pos;
		if (!landmark->getGlobalPos(landmark_global_pos))
			return false;
		mFounded = LLViewerParcelMgr::getInstance()->inAgentParcel(landmark_global_pos);
		return mFounded;
	}
};

static void fetch_landmarks(LLInventoryModel::cat_array_t& cats,
							LLInventoryModel::item_array_t& items,
							LLInventoryCollectFunctor& add)
{
	// Look in "My Favorites"
	const LLUUID favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
	gInventory.collectDescendentsIf(favorites_folder_id,
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		add);

	// Look in "Landmarks"
	const LLUUID landmarks_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
	gInventory.collectDescendentsIf(landmarks_folder_id,
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		add);
}

LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::string& name, BOOL use_substring)
{
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchLandmarksByName by_name(name, use_substring);
	fetch_landmarks(cats, items, by_name);

	return items;
}

bool LLLandmarkActions::landmarkAlreadyExists()
{
	// Determine whether there are landmarks pointing to the current global  agent position.
	return findLandmarkForAgentPos() != NULL;
}

//static
//bool LLLandmarkActions::hasParcelLandmark()
// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
bool LLLandmarkActions::hasParcelLandmark(bool request_unloaded)
// [/SL:KB]
{
//	LLFirstAgentParcelLandmark get_first_agent_landmark;
// [SL:KB] - Patch: Control-LocationInputCtrl | Checked: Catznip-6.0
	LLFirstAgentParcelLandmark get_first_agent_landmark(request_unloaded);
// [/SL:KB]
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	fetch_landmarks(cats, items, get_first_agent_landmark);
	return !items.empty();
	
}

// *TODO: This could be made more efficient by only fetching the FIRST
// landmark that meets the criteria
LLViewerInventoryItem* LLLandmarkActions::findLandmarkForGlobalPos(const LLVector3d &pos)
{
	// Determine whether there are landmarks pointing to the current parcel.
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchlLandmarkByPos is_current_pos_landmark(pos);
	fetch_landmarks(cats, items, is_current_pos_landmark);

	if(items.empty())
	{
		return NULL;
	}

	return items[0];
}

LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos()
{
	return findLandmarkForGlobalPos(gAgent.getPositionGlobal());
}

bool LLLandmarkActions::canCreateLandmarkHere()
{
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if(!agent_parcel)
	{
		LL_WARNS() << "No agent region" << LL_ENDL;
		return false;
	}
	if (agent_parcel->getAllowLandmark()
		|| LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK))
	{
		return true;
	}

	return false;
}

//void LLLandmarkActions::createLandmarkHere(
//	const std::string& name, 
//	const std::string& desc, 
//	const LLUUID& folder_id)
// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2013-08-14 (Catznip-3.6)
void LLLandmarkActions::createLandmarkHere(
	const std::string& name, 
	const std::string& desc, 
	const LLUUID& folder_id,
	LLPointer<LLInventoryCallback> cb)
// [/SL:KB]
{
	if(!gAgent.getRegion())
	{
		LL_WARNS() << "No agent region" << LL_ENDL;
		return;
	}
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if (!agent_parcel)
	{
		LL_WARNS() << "No agent parcel" << LL_ENDL;
		return;
	}
	if (!canCreateLandmarkHere())
	{
		LLNotificationsUtil::add("CannotCreateLandmarkNotOwner");
		return;
	}

// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2013-08-14 (Catznip-3.6)
	create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
		folder_id, LLTransactionID::tnull,
		name, desc,
		LLAssetType::AT_LANDMARK,
		LLInventoryType::IT_LANDMARK,
		NOT_WEARABLE, PERM_ALL, 
		cb);
// [/SL:KB]
//	create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
//		folder_id, LLTransactionID::tnull,
//		name, desc,
//		LLAssetType::AT_LANDMARK,
//		LLInventoryType::IT_LANDMARK,
//		NOT_WEARABLE, PERM_ALL, 
//		NULL);
}

//void LLLandmarkActions::createLandmarkHere()
// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2013-08-14 (Catznip-3.6)
void LLLandmarkActions::createLandmarkHere(LLPointer<LLInventoryCallback> cb)
// [/SL:KB]
{
	std::string landmark_name, landmark_desc;

	LLAgentUI::buildLocationString(landmark_name, LLAgentUI::LOCATION_FORMAT_LANDMARK);
	LLAgentUI::buildLocationString(landmark_desc, LLAgentUI::LOCATION_FORMAT_FULL);
	const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);

// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2013-08-14 (Catznip-3.6)
	createLandmarkHere(landmark_name, landmark_desc, folder_id, cb);
// [/SL:KB]
//	createLandmarkHere(landmark_name, landmark_desc, folder_id);
}

void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped /* = true */)
{
	std::string sim_name;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
	  std::string slurl = LLSLURL(sim_name, global_pos).getSLURLString();
		cb(slurl);

		return;
	}
	else
	{
		U64 new_region_handle = to_region_handle(global_pos);

		LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL,
														cb,
														global_pos,
														escaped,
														_2);

		LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
	}
}

void LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(const LLVector3d& global_pos, region_name_and_coords_callback_t cb)
{
	std::string sim_name;
	LLSimInfo* sim_infop = LLWorldMap::getInstance()->simInfoFromPosGlobal(global_pos);
	if (sim_infop)
	{
		LLVector3 pos = sim_infop->getLocalPos(global_pos);
		std::string name = sim_infop->getName() ;
		cb(name, ll_round(pos.mV[VX]), ll_round(pos.mV[VY]),ll_round(pos.mV[VZ]));
	}
	else
	{
		U64 new_region_handle = to_region_handle(global_pos);

		LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords,
														cb,
														global_pos,
														_1);

		LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
	}
}

void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb,
										 const LLVector3d& global_pos,
										 bool escaped,
										 const std::string& url)
{
	std::string sim_name;
	std::string slurl;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
	  slurl = LLSLURL(sim_name, global_pos).getSLURLString();
	}
	else
	{
		slurl = "";
	}

	cb(slurl);
}

void LLLandmarkActions::onRegionResponseNameAndCoords(region_name_and_coords_callback_t cb,
										 const LLVector3d& global_pos,
										 U64 region_handle)
{
	LLSimInfo* sim_infop = LLWorldMap::getInstance()->simInfoFromHandle(region_handle);
	if (sim_infop)
	{
		LLVector3 local_pos = sim_infop->getLocalPos(global_pos);
		std::string name = sim_infop->getName() ;
		cb(name, ll_round(local_pos.mV[VX]), ll_round(local_pos.mV[VY]), ll_round(local_pos.mV[VZ]));
	}
}

bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal)
{
	LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID);
	if (NULL == item)
		return false;

	const LLUUID& asset_id = item->getAssetUUID();

	LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL);
	if (NULL == landmark)
		return false;

	return landmark->getGlobalPos(posGlobal);
}

LLLandmark* LLLandmarkActions::getLandmark(const LLUUID& landmarkInventoryItemID, LLLandmarkList::loaded_callback_t cb)
{
	LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID);
	if (NULL == item)
		return NULL;

	const LLUUID& asset_id = item->getAssetUUID();

	LLLandmark* landmark = gLandmarkList.getAsset(asset_id, cb);
	if (landmark)
	{
		return landmark;
	}

	return NULL;
}

void LLLandmarkActions::copySLURLtoClipboard(const LLUUID& landmarkInventoryItemID)
{
	LLLandmark* landmark = LLLandmarkActions::getLandmark(landmarkInventoryItemID);
	if(landmark)
	{
		LLVector3d global_pos;
		landmark->getGlobalPos(global_pos);
		LLLandmarkActions::getSLURLfromPosGlobal(global_pos,&copy_slurl_to_clipboard_callback,true);
	}
}

void copy_slurl_to_clipboard_callback(const std::string& slurl)
{
	gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
	LLSD args;
	args["SLURL"] = slurl;
	LLNotificationsUtil::add("CopySLURL", args);
}

// [SL:KB] - Patch: UI-ParcelInfoFloater | Checked: 2013-08-14 (Catznip-3.6)
class LLShowCreatedLandmark : public LLInventoryCallback
{
public:
	LLShowCreatedLandmark() { }
	/*virtual*/ ~LLShowCreatedLandmark() { }

	/*virtual*/ void fire(const LLUUID& idItem)
	{
		LLFloaterReg::showInstance("parcel_info", LLSD().with("type", "landmark").with("id", idItem).with("action", "edit"));
	}
};

void LLLandmarkActions::showCreateLandmark()
{
	if (gSavedSettings.getBOOL("ShowPlaceFloater"))
		createLandmarkHere(new LLShowCreatedLandmark());
	else
		LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "create_landmark"));
}

void LLLandmarkActions::showLandmarkInfo(const LLUUID& idItem)
{
	LLSD sdKey;
	sdKey["type"] = "landmark";
	sdKey["id"] = idItem;

	if (gSavedSettings.getBOOL("ShowPlaceFloater"))
		LLFloaterReg::showInstance("parcel_info", sdKey);
	else
		LLFloaterSidePanelContainer::showPanel("places", sdKey);
}
// [/SL:KB]
