/** 
 * @file llviewerassetstats.h
 * @brief Client-side collection of asset request statistics
 *
 * $LicenseInfo:firstyear=2010&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_LLVIEWERASSETSTATUS_H
#define	LL_LLVIEWERASSETSTATUS_H

#include "llpointer.h"
#include "llrefcount.h"
#include "llviewerassettype.h"
#include "llviewerassetstorage.h"
#include "llsd.h"
#include "llvoavatar.h"
#include "lltrace.h"
#include "llinitparam.h"

namespace LLViewerAssetStatsFF
{
	enum EViewerAssetCategories
	{
		EVACTextureTempHTTPGet,			//< Texture GETs - temp/baked, HTTP
		EVACTextureTempUDPGet,			//< Texture GETs - temp/baked, UDP
		EVACTextureNonTempHTTPGet,		//< Texture GETs - perm, HTTP
		EVACTextureNonTempUDPGet,		//< Texture GETs - perm, UDP
		EVACWearableHTTPGet,			//< Wearable GETs HTTP
		EVACWearableUDPGet,				//< Wearable GETs UDP
		EVACSoundHTTPGet,				//< Sound GETs HTTP
		EVACSoundUDPGet,				//< Sound GETs UDP
		EVACGestureHTTPGet,				//< Gesture GETs HTTP
		EVACGestureUDPGet,				//< Gesture GETs UDP
		EVACLandmarkHTTPGet,			//< Landmark GETs HTTP
		EVACLandmarkUDPGet,				//< Landmark GETs UDP
		EVACOtherHTTPGet,				//< Other GETs HTTP
		EVACOtherUDPGet,				//< Other GETs UDP

		EVACCount						// Must be last
	};
}

/**
 * @class LLViewerAssetStats
 * @brief Records performance aspects of asset access operations.
 *
 * This facility is derived from a very similar simulator-based
 * one, LLSimAssetStats.  It's function is to count asset access
 * operations and characterize response times.  Collected data
 * are binned in several dimensions:
 *
 *  - Asset types collapsed into a few aggregated categories
 *  - By simulator UUID
 *  - By transport mechanism (HTTP vs MessageSystem)
 *  - By persistence (temp vs non-temp)
 *
 * Statistics collected are fairly basic at this point:
 *
 *  - Counts of enqueue and dequeue operations
 *  - Min/Max/Mean of asset transfer operations
 *
 * This collector differs from the simulator-based on in a
 * number of ways:
 *
 *  - The front-end/back-end distinction doesn't exist in viewer
 *    code
 *  - Multiple threads must be safely accomodated in the viewer
 *
 * Access to results is by conversion to an LLSD with some standardized
 * key names.  The intent of this structure is that it be emitted as
 * standard syslog-based metrics formatting where it can be picked
 * up by interested parties.
 *
 * For convenience, a set of free functions in namespace
 * LLViewerAssetStatsFF is provided for conditional test-and-call
 * operations.
 */

class LLViewerAssetStats : public LLStopWatchControlsMixin<LLViewerAssetStats>
{
public:
	/**
	 * Type for duration and other time values in the metrics.  Selected
	 * for compatibility with the pre-existing timestamp on the texture
	 * fetcher class, LLTextureFetch.
	 */
	typedef U64Microseconds duration_t;

	/**
	 * Type for the region identifier used in stats.  Currently uses
	 * the region handle's type (a U64) rather than the regions's LLUUID
	 * as the latter isn't available immediately.
	 */
	typedef U64 region_handle_t;

	struct AssetRequestType : public LLInitParam::Block<AssetRequestType>
	{
		Mandatory<S32>	enqueued,
						dequeued,
						resp_count;
		Mandatory<F64>	resp_min,
						resp_max,
						resp_mean,
						resp_mean_bytes;
	
		AssetRequestType();
	};

	struct FPSStats : public LLInitParam::Block<FPSStats>
    {
		Mandatory<S32>	count;
		Mandatory<F64>	min,
						max,
						mean;
		FPSStats();
	};

	struct RegionStats : public LLInitParam::Block<RegionStats>
    {
		Optional<AssetRequestType>	get_texture_temp_http,
									get_texture_temp_udp,
									get_texture_non_temp_http,
									get_texture_non_temp_udp,
									get_wearable_http,
									get_wearable_udp,
									get_sound_http,
									get_sound_udp,
									get_gesture_http,
									get_gesture_udp,
									get_landmark_http,
									get_landmark_udp,
									get_other_http,
									get_other_udp;
		Optional<FPSStats>			fps;
		Optional<S32>				grid_x,
									grid_y;
		Optional<F64>				duration;

		RegionStats();
	};
		
	struct AssetStats : public LLInitParam::Block<AssetStats>
	{
		Multiple<RegionStats>	regions;
		Mandatory<F64>			duration;
		
		Mandatory<LLUUID>		session_id,
								agent_id;
		
		Mandatory<std::string>	message;
		Mandatory<S32>			sequence;
		Mandatory<bool>			initial,
								break_;
		
		AssetStats();
	};

public:
	LLViewerAssetStats();
	LLViewerAssetStats(const LLViewerAssetStats &);

	// Default destructor is correct.
	LLViewerAssetStats & operator=(const LLViewerAssetStats &) = delete;			// Not defined

	// Clear all metrics data.  This leaves the currently-active region
	// in place but with zero'd data for all metrics.  All other regions
	// are removed from the collection map.
	void reset();

	// Set hidden region argument and establish context for subsequent
	// collection calls.
	void setRegion(region_handle_t region_handle);

	// Retrieve current metrics for all visited regions (NULL region UUID/handle excluded)
    // Uses AssetStats structure seen above
	void getStats(AssetStats& stats, bool compact_output);

    // Retrieve a single asset request type (taken from a single region)
    template <typename T>
    void getStat(LLTrace::Recording& rec, T& req, LLViewerAssetStatsFF::EViewerAssetCategories cat, bool compact_output);

	LLSD asLLSD(bool compact_output);

protected:
	void handleStart() override;
	void handleStop() override;
	void handleReset() override;

	typedef std::map<region_handle_t, LLTrace::Recording > PerRegionRecordingContainer;

	// Region of the currently-active region.  Always valid but may
	// be zero after construction or when explicitly set.  Unchanged
	// by a reset() call.
	region_handle_t mRegionHandle;

	// Pointer to metrics collection for currently-active region.  
	LLTrace::Recording*			mCurRecording;

	// Metrics data for all regions during one collection cycle
	PerRegionRecordingContainer mRegionRecordings;
};


/**
 * Global stats collectors one for each independent thread where
 * assets and other statistics are gathered.  The globals are
 * expected to be created at startup time and then picked up by
 * their respective threads afterwards.  A set of free functions
 * are provided to access methods behind the globals while both
 * minimally disrupting visual flow and supplying a description
 * of intent.
 *
 * Expected thread assignments:
 *
 *  - Main:  main() program execution thread
 *  - Thread1:  TextureFetch worker thread
 */
extern LLViewerAssetStats * gViewerAssetStats;

namespace LLViewerAssetStatsFF
{

/**
 * @brief Allocation and deallocation of globals.
 *
 * init() should be called before threads are started that will access it though
 * you'll likely get away with calling it afterwards.  cleanup() should only be
 * called after threads are shutdown to prevent races on the global pointers.
 */
void init();

void cleanup();

/**
 * We have many timers, clocks etc. in the runtime.  This is the
 * canonical timestamp for these metrics which is compatible with
 * the pre-existing timestamping in the texture fetcher.
 */
inline LLViewerAssetStats::duration_t get_timestamp()
{
	return LLTimer::getTotalTime();
}

/**
 * Region context, event and duration loggers for the Main thread.
 */
void set_region(LLViewerAssetStats::region_handle_t region_handle);

void record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp);

void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp);

void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp,
                     LLViewerAssetStats::duration_t duration, F64 bytes=0);

void record_avatar_stats();

} // namespace LLViewerAssetStatsFF

#endif // LL_LLVIEWERASSETSTATUS_H
