/*
								+----------------------------------+
								|                                  |
								|  ***   Simple FPS counter   ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2006   |
								|                                  |
								|          FPS_Counter.h           |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __FPS_COUNTER_INCLUDED
#define __FPS_COUNTER_INCLUDED

/**
 *	@file gl2/FPS_Counter.h
 *	@author -tHE SWINe-
 *	@date 2006
 *	@brief minimalist FPS counter OpenGL widget
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@todo this code is now largerly obsolete. while design isn't the worst, implementation is.
 *		need to stop using immediate mode, and replace it by vertex arrays (be it VBO,
 *		or client-side VA).
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

/**
 *	@brief FPS counter
 *
 *	A simple class, implementing calculation of sliding average of frames per second
 *		(or another quantity). Current FPS is calculated as simple inverse time between
 *		two consecutibe frames, then there's more precise average FPS, given by ratio
 *		of number of frames, rendered in a certain time span to elapsed time, it updates
 *		once at the end of every time span. Finally, there's sliding average FPS, which
 *		is based on current FPS and updates every frame. Sliding average is implemented
 *		as @f$x_t+1 = x_t * k + x_c * (1 - k)@f$, where @f$x_t, x_t+1@f$ are
 *		current and next sliding average FPS, respectively, @f$x_c@f$ is current FPS
 *		and finally @f$k@f$ is coefficient, controlling sliding average reaction speed.
 *		Note the sliding average actually depends on update (= frame) rate.
 */
class CFPSCounter {
private:
	int m_n_frame_num;
	float m_f_last_fps_time;
	float m_f_last_frame_time;
	float m_f_fps_avg;
	float m_f_fps_savg;
	float m_f_fps_cur;
	float m_f_avg_fps_poll_time;
	float m_f_avg_slide_coeff;

public:
	/**
	 *	@brief default constructor
	 *
	 *	Creates a new FPS counter object.
	 *
	 *	@param[in] f_avg_fps_poll_time is period (in seconds) by which average FPS will
	 *		be upgraded (the bigger period, the more precise reading)
	 *	@param[in] f_avg_slide_coeff is coefficient for calculating sliding-average FPS
	 *		(it's calculated from current-frame FPS by averaging in time; the smaller number,
	 *		the slower propagation of FPS changes, but more smoother average)
	 */
	CFPSCounter(float f_avg_fps_poll_time = 1.0f, float f_avg_slide_coeff = .1f);

	/**
	 *	@brief resets internal time counters
	 *
	 *	This resets time of last frame, and framecount since then. FPS remains the same.
	 *		This may be used when time, passed to OnFrame() was lost (such as when the
	 *		timer was reset, or when the rendering was paused for some time).
	 */
	void Reset();

	/**
	 *	@brief frame notification
	 *
	 *	This function needs to be called every frame, it notifies FPS counter the next frame
	 *		was rendered, and passes the timestamp to calculate FPS from.
	 *
	 *	@param[in] f_frame_time is continous frame time in seconds
	 *
	 *	@note In case f_frame_time is less than previous frame time (might be caused by timer
	 *		reseting, etc.) FPS counter resets itself.
	 */
	void OnFrame(float f_frame_time);

	/**
	 *	@brief gets current FPS
	 *
	 *	@return Returns current (extrapolated) number of frames per second.
	 *
	 *	@note Results of this function tend to be rather noisy.
	 */
	inline float f_Cur_FPS() const
	{
		return m_f_fps_cur;
	}

	/**
	 *	@brief gets average FPS
	 *
	 *	@return Returns average number of frames per second (averaged over a given
	 *		poll interval).
	 */
	inline float f_AVG_FPS() const
	{
		return m_f_fps_avg;
	}

	/**
	 *	@brief gets sliding average FPS
	 *
	 *	@return Returns sliding average of number of frames per second.
	 */
	inline float f_Sliding_AVG_FPS() const
	{
		return m_f_fps_savg;
	}
};

/**
 *	OpenGL implementation of time-dependent graph renderer.
 *
 *	@deprecated This is now deprecated because of use of deprecated OpenGL features.
 *
 *	@todo Rename this to CGLTimeLineGraph.
 *	@todo Use vertex arrays instead of immediate mode.
 */
class CTimeLineGraph {
private:
	struct TTimeLineInfo {
	protected:
		float *p_queue;
		int n_queue_length;

	public:
		int n_id;
		float f_min_value;
		float f_max_value;
		bool b_normalize;
		bool b_solid;
		float f_opacity;
		Vector3f v_color;

		TTimeLineInfo();
		~TTimeLineInfo();
		bool Alloc(int n_size);
		int n_Length() const;
		bool AddReading(float f_value, int n_skip = 0);
		void GL_Draw(CGLState *p_state, Vector2f v_min, Vector2f v_max,
			float f_min, float f_max, float f_subframe_time) const;
		void GL_DrawFill(CGLState *p_state, Vector2f v_min, Vector2f v_max,
			float f_min, float f_max, float f_subframe_time) const;
		float f_Cur_MinSpike() const;
		float f_Cur_MaxSpike() const;
	};

	std::vector<TTimeLineInfo*> m_timeline_list; // sorted in z-order
	int m_n_queue_id_pool;

	class CIfIdEquals {
	protected:
		int m_n_id;

	public:
		inline CIfIdEquals(int n_id);
		inline bool operator ()(const TTimeLineInfo *p_info) const;
	};

	class CDeleteTimeline {
	public:
		inline void operator ()(const TTimeLineInfo *p_info) const;
		inline operator bool() const;
	};

	class CRenderTimeLine {
	protected:
		bool m_b_allow_separate_scale;
		bool m_b_allow_separate_zero;
		bool m_b_first;
		float m_f_min, m_f_max, m_f_size;
		float m_f_subframe_time;
		Vector2f m_v_min, m_v_max;
		CGLState *m_p_state;

	public:
		inline CRenderTimeLine(CGLState *p_state, bool b_allow_separate_scale,
			bool b_allow_separate_zero, Vector2f v_min, Vector2f v_max, float f_subframe_time);
		inline CRenderTimeLine(const CRenderTimeLine &r_render);
		inline void operator ()(const TTimeLineInfo *p_info);
		inline operator bool() const;
	};

	class CRenderTimeLineRender : public CRenderTimeLine {
	public:
		inline CRenderTimeLineRender(const CRenderTimeLine &r_render);
		inline void operator ()(const TTimeLineInfo *p_info);
	};

public:
	/**
	 *	@brief default constructor
	 *
	 *	Has no effect, just initializes an empty time line graph.
	 */
	CTimeLineGraph();

	/**
	 *	@brief destructor
	 */
	~CTimeLineGraph();

	/**
	 *	@brief adds a new timeline
	 *
	 *	Adds a new timeline with specified parameters.
	 *
	 *	@param[in] n_buffer_length is length of buffer (min 2)
	 *	@param[in] f_min_value is minimal graph (y) value
	 *	@param[in] f_max_value is maximal graph (y) value, timeline graph
	 *		will be stretched between f_min_value and f_min_value,
	 *		values out of range will be clamped
	 *	@param[in] b_normalize if it's true, actual min and max value
	 *		to be used for drawing will be determined from
	 *		minimal and maximal value in timeline window
	 *	@param[in] v_color is color to draw timeline with
	 *	@param[in] b_solid indicates wheter timeline should be drawn with simple line (0)
	 *		or as area, enclosed between time axis and graph line
	 *	@param[in] f_opacity indicates opacity for rendering solid timeline
	 *		(solid timeline is drawn as solid line and optionaly with
	 *		transparent fill)
	 *
	 *	@return Returns timeline id in case of success or -1 on failure.
	 */
	int n_AddTimeline(int n_buffer_length, float f_min_value,
		float f_max_value, bool b_normalize, Vector3f v_color,
		bool b_solid = false, float f_opacity = 1);

	/**
	 *	@brief valid timeline predicate
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns true if n_id specifies valid timeline. otherwise false.
	 */
	bool b_IsTimeline(int n_id);

	/**
	 *	@brief deletes timeline
	 *
	 *	Deletes timeline specified by n_id.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool DeleteTimeline(int n_id);

	/**
	 *	@brief deletes all the timelines
	 */
	void DeleteTimelines();

	/**
	 *	@brief adds a sample to timeline
	 *
	 *	Adds reading to timeline specified by n_id
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] f_value is reading value
	 *	@param[in] n_skip is number of time frames to skip (all will be set to f_value)
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool AddReading(int n_id, float f_value, int n_skip = 0);

	/**
	 *	void CTimeLineGraph::RenderTimelineWindow(GLState *p_state, Vector2f v_min,
	 *		Vector2f v_max, bool b_allow_separate_scale, bool b_allow_separate_zero,
	 *		Vector3f v_border_color, Vector3f v_back_color, float f_back_opacity,
	 *		float f_subframe_time = 0)
	 *	@brief renders timeline graph
	 *
	 *	Renders rectangular timeline window using OpenGL.
	 *
	 *	@param[in] p_state is OpenGL state manager
	 *	@param[in] v_min is one of corner coordinates of graph window
	 *	@param[in] v_max is one of corner coordinates of graph window
	 *	@param[in] b_allow_separate_scale allows different timelines to be rendered with different
	 *		(y) scale so the values in the graph might not correspond to each other
	 *	@param[in] b_allow_separate_zero allows different timelines having time axis at different
	 *		vertical position
	 *	@param[in] v_border_color is color for graph border and for time axes
	 *	@param[in] v_back_color is background color
	 *	@param[in] f_back_opacity is background alpha
	 *	@param[in] f_subframe_time is relative time until next time frame in range <0, 1> allowing
	 *		  graph to animate smoothly
	 *
	 *	@todo These parameters shouldn't really be there, they should be set to member variables
	 *		in constructor and used from there (and getters / setters should be implemented).
	 */
	void RenderTimelineWindow(CGLState *p_state, Vector2f v_min,
		Vector2f v_max, bool b_allow_separate_scale, bool b_allow_separate_zero,
		Vector3f v_border_color, Vector3f v_back_color, float f_back_opacity,
		float f_subframe_time = 0);

	/**
	 *	@brief gets minimum value from a timeline
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns minimal value from current values in timeline window for
	 *		  timeline specified by n_id.
	 *	@return Returns 0 in case n_id doesn't specify a valid timeline.
	 *
	 *	@note This is actual minimal reading value, not minimal value specified in
	 *		  n_AddTimeline() or by SetTimeline_Min().
	 */
	float f_Min_Value(int n_id);

	/**
	 *	@brief gets maximum value from a timeline
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns maximal value from current values in timeline window for
	 *		  timeline specified by n_id.
	 *	@return Returns 0 in case n_id doesn't specify a valid timeline-
	 *
	 *	@note This is actual maximal reading value, not maximal value specified in
	 *		  n_AddTimeline() or by SetTimeline_Max().
	 */
	float f_Max_Value(int n_id);

	/**
	 *	@brief gets number of timelines
	 *
	 *	@return Returns number of timelines being currently displayed.
	 */
	int n_Timeline_Num() const;

	/**
	 *	@brief gets timeline Z-order
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns z-order of timeline specified by n_id (it's order timelines are drawn in;
	 *		  it's number in range <0, n_Timeline_Num() - 1>).
	 *	@return Returns -1 in case n_id doesn't specify a valid timeline.
	 */
	int n_GetTimeline_ZOrder(int n_id) const;

	/**
	 *	@brief gets timeline buffer length
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns queue length of timeline specified by n_id.
	 *	@return Returns -1 in case n_id doesn't specify a valid timeline.
	 */
	int n_GetTimeline_Length(int n_id) const;

	/**
	 *	@brief gets timeline normalize flag
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns the in case timeline specified by n_id is to be drawn normalized.
	 *	@return Returns false in case n_id doesn't specify a valid timeline.
	 */
	bool b_GetTimeline_Normalize(int n_id) const;

	/**
	 *	@brief gets timeline solid flag
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns true in case timeline specified by n_id is to be drawn solid.
	 *	@return Returns false in case n_id doesn't specify a valid timeline.
	 */
	bool b_GetTimeline_Solid(int n_id) const;

	/**
	 *	@brief gets timeline y-range minimum
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns minimal graph value of timeline specified by n_id
	 *		(note it's not an actual value in timeline window; just
	 *		minimal value used to scale the graph in case normlization
	 *		for a given timeline is not enabled).
	 *	@return Returns 0 in case n_id doesn't specify a valid timeline.
	 */
	float f_GetTimeline_Min(int n_id) const;

	/**
	 *	@brief gets timeline y-range maximum
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns maximal graph value of timeline specified by n_id
	 *		(note it's not an actual value in timeline window; just
	 *		maximal value used to scale the graph in case normlization
	 *		for a given timeline is not enabled).
	 *	@return Returns 0 in case n_id doesn't specify a valid timeline.
	 */
	float f_GetTimeline_Max(int n_id) const;

	/**
	 *	@brief gets timeline color
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns color of timeline specified by n_id
	 *	@return Returns vector of zeros (black) in case n_id doesn't specify a valid timeline
	 */
	Vector3f v_GetTimeline_Color(int n_id) const;

	/**
	 *	@brief gets timeline alpha
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *
	 *	@return Returns opacity of timeline specified by n_id
	 *	@return Returns 0 in case n_id doesn't specify a valid timeline
	 */
	float f_GetTimeline_Opacity(int n_id) const;

	/**
	 *	@brief sets Z-order
	 *
	 *	Sets z-order of timeline, specified by n_id. Lower Z-order means timeline is going
	 *		to be closer to the background (potentially overdrawn by timelines with greater
	 *		Z-order).
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] n_z_order is new Z-order
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_ZOrder(int n_id, int n_z_order);

	/**
	 *	@brief sets buffer length
	 *
	 *	Sets length of sample buffer of timeline, specified by n_id to n_length.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] n_length is length, in samples
	 *
	 *	@return Returns true if n_id specified valid timeline and there
	 *		  was enough memory for new queue. otherwise false.
	 */
	bool SetTimeline_Length(int n_id, int n_length);

	/**
	 *	@brief enables / disables timeline normalisation
	 *
	 *	Sets normalization of timeline, specified by n_id to b_normalize
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] b_normalize is timeline normalization flag
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_Normalize(int n_id, bool b_normalize);

	/**
	 *	@brief sets fill style
	 *
	 *	Sets fill style of timeline, specified by n_id to b_solid.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] b_solid chooses between solid fill (true) / no fill (false)
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_Solid(int n_id, bool b_solid);

	/**
	 *	@brief sets minimum of y-range
	 *
	 *	Sets minimal value of graph y-axis of timeline, specified by n_id to f_min.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] f_min is minimal value to be displayed in graph window (smaller
	 *		values are clamped)
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_Min(int n_id, float f_min);

	/**
	 *	@brief sets maximum of y-range
	 *
	 *	Sets maximal value of graph y-axis of timeline, specified by n_id to f_max.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] f_max is maximal value to be displayed in graph window (greater
	 *		values are clamped)
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_Max(int n_id, float f_max);

	/**
	 *	@brief sets timeline color
	 *
	 *	Sets color of timeline, specified by n_id to v_color.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] v_color is RGB color with individual components in [0, 1] range
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 */
	bool SetTimeline_Color(int n_id, Vector3f v_color);

	/**
	 *	@brief sets timeline fill alpha
	 *
	 *	Sets opacity of fill of timeline, specified by n_id to f_opacity.
	 *
	 *	@param[in] n_id is timeline id, returned by previous call to n_AddTimeline()
	 *	@param[in] f_opacity is timeline fill alpha in [0, 1] range
	 *
	 *	@return Returns true if n_id specified valid timeline. otherwise false.
	 *
	 *	@note This alpha doesn't apply to line, which is always fully opaque.
	 *		It only applies to fill.
	 */
	bool SetTimeline_Opacity(int n_id, float f_opacity);

private:
	inline std::vector<TTimeLineInfo*>::iterator p_TimeLine(int n_id);
	inline std::vector<TTimeLineInfo*>::const_iterator p_TimeLine(int n_id) const;
};

#endif // !__FPS_COUNTER_INCLUDED
