/** 
 * @file llteleporthistory.h
 * @brief Teleport history
 *
 * $LicenseInfo:firstyear=2009&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_LLTELEPORTHISTORY_H
#define LL_LLTELEPORTHISTORY_H

#include "llsingleton.h" // for LLSingleton

#include <vector>
#include <string>
#include <boost/function.hpp>
#include <boost/signals2.hpp>


/**
 * An item of the teleport history.
 * 
 * Contains the location's global coordinates and its title.
 */
class LLTeleportHistoryItem
{
public:
	LLTeleportHistoryItem()
	{}

	LLTeleportHistoryItem(std::string title, LLVector3d global_pos)
		: mTitle(title), mGlobalPos(global_pos)
	{}

	/**
	 * @return title formatted according to the current value of the 
	 * NavBarShowCoordinates setting.
	 */
	const std::string& getTitle() const;
	
	std::string	mTitle;		// human-readable location title
	std::string mFullTitle; // human-readable location title including coordinates
	LLVector3d	mGlobalPos; // global position
	LLUUID		mRegionID;	// region ID for getting the region info 
};

/**
 * Teleport history.
 * 
 * Along with the navigation bar "Back" and "Forward" buttons
 * implements web browser-like navigation functionality.
 * 
 * @see LLNavigationBar
 */
class LLTeleportHistory: public LLSingleton<LLTeleportHistory>
{
	LOG_CLASS(LLTeleportHistory);

public:
	
	typedef std::vector<LLTeleportHistoryItem>	slurl_list_t;
	typedef boost::function<void()>				history_callback_t;
	typedef boost::signals2::signal<void()>		history_signal_t;
	
	LLTeleportHistory();
	~LLTeleportHistory();

	/**
	 * Go back in the history.
	 */
	void					goBack() { goToItem(getCurrentItemIndex() - 1); }
	
	/**
	 * Go forward in the history.
	 */
	void					goForward() { goToItem(getCurrentItemIndex() + 1); }
	
	/**
	 * Go to specific item in the history.
	 * 
	 * The item is specified by its index (starting from 0).
	 */ 
	void					goToItem(int idx);
	
	/**
	 * @return history items.
	 */
	const slurl_list_t&		getItems() const { return mItems; }
	void                    purgeItems();
	/**
	 * Is the history empty?
	 * 
	 * History containing single item is treated as empty
	 * because the item points to the current location.
	 */ 
	bool					isEmpty() const { return mItems.size() <= 1; }
	
	/**
	 * Get index of the current location in the history.
	 */
	int						getCurrentItemIndex() const { return mCurrentItem; }
	/**
	 * Set a callback to be called upon history changes.
	 * 
	 * Multiple callbacks can be set.
	 */
	boost::signals2::connection	setHistoryChangedCallback(history_callback_t cb);
	
	/**
	 * Save history to a file so that we can restore it on startup.
	 * 
	 * @see load()
	 */
	void					dump() const;
	/**
	 * Process login complete event. Basically put current location into history
	 */
	void					handleLoginComplete();

private:
	
	/**
	 * Called by when a teleport fails.
	 * 
	 * Called via callback set on the LLViewerParcelMgr "teleport failed" signal.
	 * 
	 * @see mTeleportFailedConn
	 */
	void onTeleportFailed();

	/**
	 * Update current location.
	 * 
	 * @param new_pos Current agent global position. After local teleports we
	 *                cannot rely on gAgent.getPositionGlobal(),
	 *                so the new position gets passed explicitly.
	 * 
	 * Called when a teleport finishes.
	 * Called via callback set on the LLViewerParcelMgr "teleport finished" signal.
	 *
	 * Takes mRequestedItem into consideration: if it's not -1
	 * (i.e. user is teleporting to an arbitrary location, not to a history item)
	 * we purge forward items and append a new one, making it current. Otherwise
	 * we just modify mCurrentItem.
	 * 
	 * @see mRequestedItem
	 * @see mGotInitialUpdate
	 */
	void					updateCurrentLocation(const LLVector3d& new_pos);
	
	/**
	 * Invokes the "history changed" callback(s).
	 */
	void					onHistoryChanged();

	/**
	 * Format current agent location in a human-readable manner.
	 * 
	 * @param full whether to include coordinates
	 * @param local_pos_override hack: see description of updateCurrentLocation()
	 * @return
	 */
	static std::string		getCurrentLocationTitle(bool full, const LLVector3& local_pos_override);
	
	/**
	 * Actually, the teleport history.
	 */
	slurl_list_t			mItems;
	
	/**
	 * Current position within the history.
	 */
	int						mCurrentItem;
	
	/**
	 * Requested position within the history.
	 * 
	 * When a teleport succeeds, this is checked by updateCurrentLocation() to tell
	 * if this is a teleport within the history (mRequestedItem >=0) or not (-1).
	 * 
	 * Set by goToItem(); reset by onTeleportFailed() (if teleport fails).
	 * 
	 * @see goToItem()
	 * @see updateCurrentLocation()
	 */
	int						mRequestedItem;
	
	/**
	 * Have we received the initial location update?
	 * 
	 * @see updateCurrentLocation()
	 */
	bool					mGotInitialUpdate;
	
	/**
	 * Signal emitted when the history gets changed.
	 * 
	 * Invokes callbacks set with setHistoryChangedCallback().
	 */
	history_signal_t		mHistoryChangedSignal;
	
	/**
	 * Teleport success notification connection.
	 * 
	 * Using this connection we get notified when a teleport finishes
	 * or initial location update occurs.
	 */
	boost::signals2::connection	mTeleportFinishedConn;
	
	/**
	 * Teleport failure notification connection.
	 * 
	 * Using this connection we get notified when a teleport fails.
	 */
	boost::signals2::connection	mTeleportFailedConn;
};

#endif