/** 
 * @file llcallingcard.h
 * @brief Definition of the LLPreviewCallingCard class
 *
 * $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$
 */

#ifndef LL_LLCALLINGCARD_H
#define LL_LLCALLINGCARD_H

#include <map>
#include <set>
#include <string>
#include <vector>
#include "lluserrelations.h"
#include "lluuid.h"
#include "v3dmath.h"

//class LLInventoryModel;
//class LLInventoryObserver;
class LLMessageSystem;
class LLTrackingData;

class LLFriendObserver
{
public:
	// This enumeration is a way to refer to what changed in a more
	// human readable format. You can mask the value provided by
	// changed() to see if the observer is interested in the change.
	enum 
	{
		NONE = 0,
		ADD = 1,
		REMOVE = 2,
		ONLINE = 4,
		POWERS = 8,
		ALL = 0xffffffff
	};
	virtual ~LLFriendObserver() {}
	virtual void changed(U32 mask) = 0;
};

/*
struct LLBuddyInfo
{
	bool mIsOnline;
	bool mIsEmpowered;
	LLBuddyInfo() : mIsOnline(false), mIsEmpowered(false) {}
};
*/

// This is used as a base class for doing operations on all buddies.
class LLRelationshipFunctor
{
public:
	virtual ~LLRelationshipFunctor() {}
	virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy) = 0;
};
	

class LLAvatarTracker
{
public:
	static LLAvatarTracker& instance() { return sInstance; }
	
	void track(const LLUUID& avatar_id, const std::string& name);
	void untrack(const LLUUID& avatar_id);
	bool isTrackedAgentValid() { return mTrackedAgentValid; }
	void setTrackedAgentValid(bool valid) { mTrackedAgentValid = valid; }
	void findAgent();

	// coarse update information
	void setTrackedCoarseLocation(const LLVector3d& global_pos);

	// dealing with the tracked agent location
	bool haveTrackingInfo();
	void getDegreesAndDist(F32& rot, F64& horiz_dist, F64& vert_dist);
	LLVector3d getGlobalPos();

	// Get the name passed in, returns null string if uninitialized.
	const std::string& getName();

	// Get the avatar being tracked, returns LLUUID::null if uninitialized
	const LLUUID& getAvatarID();

	// Deal with inventory
	//void observe(LLInventoryModel* model);
	//void inventoryChanged();

	// add or remove agents from buddy list. Each method takes a set
	// of buddies and returns how many were actually added or removed.
	typedef std::map<LLUUID, LLRelationship*> buddy_map_t;

	S32 addBuddyList(const buddy_map_t& buddies);
	//S32 removeBuddyList(const buddy_list_t& exes);
	void copyBuddyList(buddy_map_t& buddies) const;

	// deal with termination of friendhsip
	void terminateBuddy(const LLUUID& id);

	// get full info
	const LLRelationship* getBuddyInfo(const LLUUID& id) const;

	// Is this person a friend/buddy/calling card holder?
	bool isBuddy(const LLUUID& id) const;

	// online status
	void setBuddyOnline(const LLUUID& id, bool is_online);
	bool isBuddyOnline(const LLUUID& id) const;

	// simple empowered status
	void setBuddyEmpowered(const LLUUID& id, bool is_empowered);
	bool isBuddyEmpowered(const LLUUID& id) const;

	// set the empower bit & message the server.
	void empowerList(const buddy_map_t& list, bool grant);
	void empower(const LLUUID& id, bool grant); // wrapper for above

	// register callbacks
	void registerCallbacks(LLMessageSystem* msg);

	// Add/remove an observer. If the observer is destroyed, be sure
	// to remove it. On destruction of the tracker, it will delete any
	// observers left behind.
	void addObserver(LLFriendObserver* observer);
	void removeObserver(LLFriendObserver* observer);
	void idleNotifyObservers();
	void notifyObservers();

	// Observers interested in updates of a particular avatar.
	// On destruction these observers are NOT deleted.
	void addParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer);
	void removeParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer);
	void notifyParticularFriendObservers(const LLUUID& buddy_id);

	/**
	 * Stores flag for change and id of object change applies to
	 *
	 * This allows outsiders to tell the AvatarTracker if something has
	 * been changed 'under the hood',
	 * and next notification will have exact avatar IDs have been changed.
	 */
	void addChangedMask(U32 mask, const LLUUID& referent);

	const std::set<LLUUID>& getChangedIDs() { return mChangedBuddyIDs; }

	// Apply the functor to every buddy. Do not actually modify the
	// buddy list in the functor or bad things will happen.
	void applyFunctor(LLRelationshipFunctor& f);

	static void formFriendship(const LLUUID& friend_id);

protected:
	void deleteTrackingData();
	void agentFound(const LLUUID& prey,
					const LLVector3d& estimated_global_pos);

	// Message system functionality
	static void processAgentFound(LLMessageSystem* msg, void**);
	static void processOnlineNotification(LLMessageSystem* msg, void**);
	static void processOfflineNotification(LLMessageSystem* msg, void**);
	//static void processGrantedProxies(LLMessageSystem* msg, void**);
	static void processTerminateFriendship(LLMessageSystem* msg, void**);
	static void processChangeUserRights(LLMessageSystem* msg, void**);

	void processNotify(LLMessageSystem* msg, bool online);
	void processChange(LLMessageSystem* msg);

protected:
	static LLAvatarTracker sInstance;
	LLTrackingData* mTrackingData;
	bool mTrackedAgentValid;
	U32 mModifyMask;
	//LLInventoryModel* mInventory;
	//LLInventoryObserver* mInventoryObserver;

	buddy_map_t mBuddyInfo;

	typedef std::set<LLUUID> changed_buddy_t;
	changed_buddy_t mChangedBuddyIDs;

	typedef std::vector<LLFriendObserver*> observer_list_t;
	observer_list_t mObservers;

    typedef std::set<LLFriendObserver*> observer_set_t;
    typedef std::map<LLUUID, observer_set_t> observer_map_t;
    observer_map_t mParticularFriendObserverMap;

private:
	// do not implement
	LLAvatarTracker(const LLAvatarTracker&);
	bool operator==(const LLAvatarTracker&);

	BOOL mIsNotifyObservers;

public:
	// don't you dare create or delete this object
	LLAvatarTracker();
	~LLAvatarTracker();
};

// collect set of LLUUIDs we're a proxy for
class LLCollectProxyBuddies : public LLRelationshipFunctor
{
public:
	LLCollectProxyBuddies() {}
	virtual ~LLCollectProxyBuddies() {}
	virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
	typedef std::set<LLUUID> buddy_list_t;
	buddy_list_t mProxy;
};

// collect dictionary sorted map of name -> agent_id for every online buddy
class LLCollectMappableBuddies : public LLRelationshipFunctor
{
public:
	LLCollectMappableBuddies() {}
	virtual ~LLCollectMappableBuddies() {}
	virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
	typedef std::map<LLUUID, std::string> buddy_map_t;
	buddy_map_t mMappable;
	std::string mFullName;
};

// collect dictionary sorted map of name -> agent_id for every online buddy
class LLCollectOnlineBuddies : public LLRelationshipFunctor
{
public:
	LLCollectOnlineBuddies() {}
	virtual ~LLCollectOnlineBuddies() {}
	virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
	typedef std::map<LLUUID, std::string> buddy_map_t;
	buddy_map_t mOnline;
	std::string mFullName;
};

// collect dictionary sorted map of name -> agent_id for every buddy,
// one map is offline and the other map is online.
class LLCollectAllBuddies : public LLRelationshipFunctor
{
public:
	LLCollectAllBuddies() {}
	virtual ~LLCollectAllBuddies() {}
	virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
	typedef std::map<LLUUID, std::string> buddy_map_t;
	buddy_map_t mOnline;
	buddy_map_t mOffline;
	std::string mFullName;
};

#endif // LL_LLCALLINGCARD_H