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

/**
 *	@file gl2/FPS_Counter.cpp
 *	@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_*
 *
 */

#include "../NewFix.h"

#include "../CallStack.h"
#include <vector>
#include <algorithm>
#include <functional>
#include <math.h>
#include "OpenGL20.h"
#include "OpenGLState.h"
#include "OpenGLDrv.h"
#include "../Vector.h"
#include "FPS_Counter.h"
#include "../StlUtils.h"

/*
 *								=== CFPSCounter ===
 */

/*
 *	CFPSCounter::CFPSCounter(float f_avg_fps_poll_time = 1.0f, f_avg_slide_coeff = .05f)
 *		- default constructor
 *		- f_avg_fps_poll_time is period (in seconds) by which average FPS will be upgraded
 *		  (the bigger period, the more precise reading)
 *		- 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::CFPSCounter(float f_avg_fps_poll_time, float f_avg_slide_coeff)
	:m_n_frame_num(0), m_f_last_fps_time(-1), m_f_last_frame_time(-1),
	m_f_fps_avg(0), m_f_fps_savg(0), m_f_fps_cur(0),
	m_f_avg_fps_poll_time(f_avg_fps_poll_time),
	m_f_avg_slide_coeff(f_avg_slide_coeff)
{
}

/*
 *	void CFPSCounter::Reset()
 *		- resets internal time counters
 */
void CFPSCounter::Reset()
{
	m_n_frame_num = 0;
	m_f_last_fps_time = -1;
	m_f_last_frame_time = -1;
	/*m_f_fps_avg = 0;
	m_f_fps_savg 0;
	m_f_fps_cur = 0;*/
}

/*
 *	void CFPSCounter::OnFrame(float f_frame_time)
 *		- this function needs to be called every frame
 *		- f_frame_time is continous frame-time
 *		- in case f_frame_time is less than previous frame time
 *		  (might be caused by timer reseting, etc.) FPS counter resets itself
 */
void CFPSCounter::OnFrame(float f_frame_time)
{
	if(f_frame_time < m_f_last_frame_time)
		Reset();

	if((f_frame_time - m_f_last_fps_time) >= m_f_avg_fps_poll_time) {
		m_f_fps_avg = m_n_frame_num / (f_frame_time - m_f_last_fps_time);
		m_n_frame_num = 0;
		m_f_last_fps_time = f_frame_time;
	}
	++ m_n_frame_num;
	// calculate avg fps

	m_f_fps_cur = 1 / (f_frame_time - m_f_last_frame_time);
	m_f_last_frame_time = f_frame_time;
	// calculate cur fps

	m_f_fps_savg = m_f_fps_savg * (1 - m_f_avg_slide_coeff) +
		m_f_fps_cur * m_f_avg_slide_coeff;
	// calculate sliding avg fps
}

/*
 *								=== ~CFPSCounter ===
 */

/*
 *								=== CTimeLineGraph::TTimeLineInfo ===
 */

/*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);
	};*/

/*
 *	CTimeLineGraph::TTimeLineInfo::TTimeLineInfo()
 *		- default constructor
 */
CTimeLineGraph::TTimeLineInfo::TTimeLineInfo()
	:p_queue(0), n_queue_length(0)
{
}

/*
 *	CTimeLineGraph::TTimeLineInfo::~TTimeLineInfo()
 *		- default destructor
 */
CTimeLineGraph::TTimeLineInfo::~TTimeLineInfo()
{
	if(p_queue)
		delete[] p_queue;
}

/*
 *	bool CTimeLineGraph::TTimeLineInfo::Alloc(int n_size)
 *		- returns true in case allocation was successful, otherwise false
 *		- note in case queue was allocated, copy old data into the new queue
 */
bool CTimeLineGraph::TTimeLineInfo::Alloc(int n_size)
{
	if(n_size < 2)
		return false;
	if(n_size == n_queue_length)
		return true;

	float *p_new_queue;
	if(!(p_new_queue = new(std::nothrow) float[n_size]))
		return false;
	// alloc
	
	if(n_size > n_queue_length)
		memset(p_new_queue + n_queue_length, 0, (n_size - n_queue_length) * sizeof(float));
	// clear end

	memcpy(p_new_queue, p_queue, ((n_queue_length > n_size)?
		n_size : n_queue_length) * sizeof(float));
	// copy old to the beginning

	if(p_queue)
		delete[] p_queue;
	n_queue_length = n_size;
	p_queue = p_new_queue;
	// update self

	return true;
}

/*
 *	int CTimeLineGraph::TTimeLineInfo::n_Length() const
 *		- return queue length
 */
int CTimeLineGraph::TTimeLineInfo::n_Length() const
{
	return n_queue_length;
}

/*
 *	bool CTimeLineGraph::TTimeLineInfo::AddReading(float f_value, int n_skip)
 *		- adds reading into the queue
 *		- n_skip is number of additional records to fill
 */
bool CTimeLineGraph::TTimeLineInfo::AddReading(float f_value, int n_skip)
{
	if(!n_queue_length)
		return false;

	n_skip = (n_skip >= n_queue_length)? n_queue_length : n_skip + 1;
	// calc number of frames to shift & fill

	for(float *p_dest = p_queue + n_queue_length - 1,
	   *p_src = p_queue + n_queue_length - n_skip - 1; p_src >= p_queue; -- p_src, -- p_dest)
		*p_dest = *p_src;
	// shift queue

	for(float *p_dest = p_queue, *p_end = p_queue + n_skip; p_dest < p_end; ++ p_dest)
		*p_dest = f_value;
	// fill new frames

	return true;
}

/*
 *	float CTimeLineGraph::TTimeLineInfo::f_Cur_MinSpike() const
 *		- returns minimal value in current queue or 0 for empty queue
 */
float CTimeLineGraph::TTimeLineInfo::f_Cur_MinSpike() const
{
	if(!n_queue_length)
		return .0f;
	float f_min = *p_queue;
	for(const float *p_src = p_queue + 1, *p_end = p_queue + n_queue_length;
	   p_src < p_end; ++ p_src) {
		if(f_min > *p_src)
			f_min = *p_src;
	}
	return f_min;
}

/*
 *	float CTimeLineGraph::TTimeLineInfo::f_Cur_MaxSpike() const
 *		- returns maximal value in current queue or 0 for empty queue
 */
float CTimeLineGraph::TTimeLineInfo::f_Cur_MaxSpike() const
{
	if(!n_queue_length)
		return .0f;
	float f_max = *p_queue;
	for(const float *p_src = p_queue + 1, *p_end = p_queue + n_queue_length;
	   p_src < p_end; ++ p_src) {
		if(f_max < *p_src)
			f_max = *p_src;
	}
	return f_max;
}

/*
 *	void CTimeLineGraph::TTimeLineInfo::GL_Draw(CGLState *p_state, Vector2f v_min, Vector2f v_max,
 *		float f_min, float f_max) const
 *		- draw timeline, clamped into a given rectangle
 *		- does no OpenGL setup, just calls glBegin(GL_LINE_STRIP), glEnd() and glVertex2f()
 */
void CTimeLineGraph::TTimeLineInfo::GL_Draw(CGLState *p_state, Vector2f v_min, Vector2f v_max,
	float f_min, float f_max, float f_subframe_time) const
{
	f_max -= f_min;
	// calculate difference

	glBegin(GL_LINE_STRIP);
	float f_x = v_min.x, f_x_step = (v_max.x - v_min.x) / (n_queue_length - 1);
	for(const float *p_src = p_queue, *p_end = p_queue + n_queue_length;
	   p_src < p_end; ++ p_src, f_x += f_x_step) {
		float f_y = (*p_src - f_min) / f_max;
		f_y = (f_y < 0)? v_min.y : ((f_y > 1)? v_max.y : v_min.y + (v_max.y - v_min.y) * f_y);
		// calculate position

		if(f_x > v_max.x)
			f_x = v_max.x;

		glVertex2f(f_x, f_y);

		if(f_x == v_min.x)
			f_x += f_subframe_time * f_x_step;
	}
	glEnd();
	// draw graph line
}

/*
 *	void CTimeLineGraph::TTimeLineInfo::GL_DrawFill(CGLState *p_state, Vector2f v_min, Vector2f v_max,
 *		float f_min, float f_max) const
 *		- draw several convex polygons, given by zero axis and timeline, clamped into a given
 *		  rectangle
 *		- does no OpenGL setup, just calls glBegin(GL_POLYGON), glEnd() and glVertex2f()
 */
void CTimeLineGraph::TTimeLineInfo::GL_DrawFill(CGLState *p_state, Vector2f v_min, Vector2f v_max,
	float f_min, float f_max, float f_subframe_time) const
{
	float f_zero_position = v_min.y - ((v_max.y - v_min.y) / (f_max - f_min)) * f_min;
	f_zero_position = (f_zero_position < v_min.y)? v_min.y :
		((f_zero_position > v_max.y)? v_max.y : f_zero_position);
	// zero position in graph space

	f_max -= f_min;
	// calculate difference

	bool b_cull_enabled = p_state->p_State()->b_cull_face;
	if(b_cull_enabled)
		p_state->DisableCullFace();

	float f_prev_x, f_prev_y;
	float f_x = v_min.x, f_x_step = (v_max.x - v_min.x) / (n_queue_length - 1);
	for(const float *p_src = p_queue, *p_end = p_queue + n_queue_length;
	   p_src < p_end; ++ p_src, f_x += f_x_step) {
		float f_y = (*p_src - f_min) / f_max;
		float f_screen_y = (f_y < 0)? v_min.y : ((f_y > 1)? v_max.y :
			v_min.y + (v_max.y - v_min.y) * f_y);
		// calculate position

		if(f_x > v_max.x)
			f_x = v_max.x;

		if(f_x > v_min.x) {
			if((f_screen_y >= f_zero_position && f_prev_y < f_zero_position) ||
			   (f_screen_y <= f_zero_position && f_prev_y > f_zero_position)) {
				float f_dxdy = (f_prev_x - f_x) / (f_prev_y - f_screen_y);
				float f_split_x = f_prev_x + (f_zero_position - f_prev_y) * f_dxdy;
				glBegin(GL_TRIANGLES);
				glVertex2f(f_prev_x, f_prev_y);
				glVertex2f(f_split_x, f_zero_position);
				glVertex2f(f_prev_x, f_zero_position);
				glVertex2f(f_x, f_screen_y);
				glVertex2f(f_x, f_zero_position);
				glVertex2f(f_split_x, f_zero_position);
				glEnd();
			} else {
				glBegin(GL_QUADS);
				glVertex2f(f_x, f_screen_y);
				glVertex2f(f_prev_x, f_prev_y);
				glVertex2f(f_prev_x, f_zero_position);
				glVertex2f(f_x, f_zero_position);
				glEnd();
			}
		}
		// check for zero crossing

		f_prev_x = f_x;
		if(f_x == v_min.x)
			f_x += f_subframe_time * f_x_step;
		f_prev_y = f_screen_y;
	}
	// draw graph fill

	if(b_cull_enabled)
		p_state->EnableCullFace();
}

/*
 *								=== ~CTimeLineGraph::TTimeLineInfo ===
 */

/*
 *								=== CTimeLineGraph::CIfIdEquals ===
 */

CTimeLineGraph::CIfIdEquals::CIfIdEquals(int n_id)
	:m_n_id(n_id)
{}

bool CTimeLineGraph::CIfIdEquals::operator ()(const TTimeLineInfo *p_info) const
{
	return p_info->n_id == m_n_id;
}

/*
 *								=== ~CTimeLineGraph::CIfIdEquals ===
 */

/*
 *								=== CTimeLineGraph::CDeleteTimeline ===
 */

void CTimeLineGraph::CDeleteTimeline::operator ()(const TTimeLineInfo *p_info) const
{
	delete (TTimeLineInfo*)p_info;
}

CTimeLineGraph::CDeleteTimeline::operator bool() const
{
	return true;
}

/*
 *								=== ~CTimeLineGraph::CDeleteTimeline ===
 */

/*
 *								=== CTimeLineGraph::CRenderTimeLine ===
 */

CTimeLineGraph::CRenderTimeLine::CRenderTimeLine(CGLState *p_state, bool b_allow_separate_scale,
	bool b_allow_separate_zero, Vector2f v_min, Vector2f v_max, float f_subframe_time)
	:m_p_state(p_state), m_b_allow_separate_scale(b_allow_separate_scale),
	m_b_allow_separate_zero(b_allow_separate_zero),
	m_b_first(true), m_v_min(v_min), m_v_max(v_max), m_f_subframe_time(f_subframe_time)
{}

CTimeLineGraph::CRenderTimeLine::CRenderTimeLine(const CRenderTimeLine &r_render)
	:m_p_state(r_render.m_p_state),
	m_b_allow_separate_scale(r_render.m_b_allow_separate_scale),
	m_b_allow_separate_zero(r_render.m_b_allow_separate_zero),
	m_v_min(r_render.m_v_min), m_v_max(r_render.m_v_max),
	m_f_min(r_render.m_f_min), m_f_max(r_render.m_f_max), m_f_size(r_render.m_f_size),
	m_f_subframe_time(r_render.m_f_subframe_time)
{}

void CTimeLineGraph::CRenderTimeLine::operator ()(const TTimeLineInfo *p_info)
{
	float f_min = (p_info->b_normalize)? p_info->f_Cur_MinSpike() : p_info->f_min_value;
	float f_max = (p_info->b_normalize)? p_info->f_Cur_MaxSpike() : p_info->f_max_value;
	if(m_f_max < f_max || m_b_first)
		m_f_max = f_max;
	if(m_f_min > f_min || m_b_first)
		m_f_min = f_min;
	if(m_f_size < (f_max - f_min) || m_b_first)
		m_f_size = f_max - f_min;
	m_b_first = false;
}

CTimeLineGraph::CRenderTimeLine::operator bool() const
{
	return true;
}

/*
 *								=== ~CTimeLineGraph::CRenderTimeLine ===
 */

/*
 *								=== CTimeLineGraph::CRenderTimeLineRender ===
 */

CTimeLineGraph::CRenderTimeLineRender::CRenderTimeLineRender(const CRenderTimeLine &r_render)
	:CRenderTimeLine(r_render)
{}

void CTimeLineGraph::CRenderTimeLineRender::operator ()(const TTimeLineInfo *p_info)
{
	float f_min = (p_info->b_normalize)? p_info->f_Cur_MinSpike() : p_info->f_min_value;
	float f_max = (p_info->b_normalize)? p_info->f_Cur_MaxSpike() : p_info->f_max_value;
	float f_abs_min = f_min;
	float f_abs_max = f_max;

	if(!m_b_allow_separate_zero) {
		if(!m_b_allow_separate_scale) {
			f_min = (f_min - m_f_min) / m_f_size;
			f_max = (f_max - m_f_min) / m_f_size;
			// just scale down to fit the window
		} else {
			f_min = (f_min - m_f_min) / m_f_size;
			f_max = (f_max - m_f_min) / m_f_size;
			// just scale down to fit the window

			if(f_min < 0 && f_max > 0) { // always want to see the axis
				float f_zero_pos = m_f_min / m_f_size;
				// where's zero? (relative, 0-1)

				float f_min_when_max_sticked = (f_min - f_zero_pos) *
					((1 - f_zero_pos) / (f_max - f_zero_pos)) + f_zero_pos;
				float f_max_when_min_sticked = (f_max - f_zero_pos) *
					((f_zero_pos) / (f_zero_pos - f_min)) + f_zero_pos;
				// try to maximize either way

				if(f_min_when_max_sticked >= 0 && f_min_when_max_sticked <= 1) {
					f_min = f_min_when_max_sticked;
					f_max = 1;
				} else if(f_max_when_min_sticked <= 1 && f_max_when_min_sticked >= 0) {
					f_min = 0;
					f_max = f_max_when_min_sticked;
				}
				// stick if possible
			}
		}
	} else {
		if(!m_b_allow_separate_scale) {
			f_max = (f_max - f_min) / m_f_size;
			f_min = 0;
			// lay everything down
		} else {
			f_min = 0;
			f_max = 1;
			// scale to best fit
		}
	}
	// calculate relative size (0 - 1)

	if(p_info->b_solid && p_info->f_opacity > 0) {
		m_p_state->Color4f(p_info->v_color.x, p_info->v_color.y,
			p_info->v_color.z, p_info->f_opacity);

		p_info->GL_DrawFill(m_p_state, Vector2f(m_v_min.x, m_v_min.y +
			(m_v_max.y - m_v_min.y) * f_min), Vector2f(m_v_max.x, m_v_min.y +
			(m_v_max.y - m_v_min.y) * f_max), f_abs_min, f_abs_max, m_f_subframe_time);
		// draw fill into the scaled rectangle
	}

	m_p_state->Color4f(p_info->v_color.x, p_info->v_color.y, p_info->v_color.z, 1);

	p_info->GL_Draw(m_p_state, Vector2f(m_v_min.x, m_v_min.y +
		(m_v_max.y - m_v_min.y) * f_min), Vector2f(m_v_max.x, m_v_min.y +
		(m_v_max.y - m_v_min.y) * f_max), f_abs_min, f_abs_max, m_f_subframe_time);
	// draw into the scaled rectangle
}

/*
 *								=== ~CTimeLineGraph::CRenderTimeLineRender ===
 */

/*
 *								=== CTimeLineGraph ===
 */

/*
 *	CTimeLineGraph::CTimeLineGraph()
 *		- default constructor
 */
CTimeLineGraph::CTimeLineGraph()
	:m_n_queue_id_pool(0)
{
}

/*
 *	CTimeLineGraph::~CTimeLineGraph()
 *		- default destructor
 */
CTimeLineGraph::~CTimeLineGraph()
{
	DeleteTimelines();
}

/*
 *	int CTimeLineGraph::n_AddTimeline(int n_buffer_length, float f_min_value,
 *		float f_max_value, bool b_normalize, Vector3f v_color,
 *		bool b_solid, float f_opacity)
 *		- add a new timeline
 *		- n_buffer_length is length of buffer (min 2)
 *		- f_min_value and f_max_value are min/max values,
 *		  timeline graph will be stretched between those two,
 *		  values out of range will be clamped
 *		- if b_normlize is true, actual min and max value
 *		  to be used for drawing will be determined from
 *		  minimal and maximal value in timeline window
 *		- v_color is color to draw timeline with
 *		- b_solid indicates wheter timeline should be drawn with simple line (0)
 *		  or as area, enclosed between time axis and graph line
 *		- f_opacity indicates opacity for rendering solid timeline
 *		  (solid timeline is drawn as solid line and optionaly with
 *		  transparent fill)
 *		- returns timeline id in case of success or -1 on failure
 */
int CTimeLineGraph::n_AddTimeline(int n_buffer_length, float f_min_value,
	float f_max_value, bool b_normalize, Vector3f v_color,
	bool b_solid, float f_opacity)
{
	if(!stl_ut::Reserve_1More(m_timeline_list))
		return -1;
	// make sure there is enough space in the list

	TTimeLineInfo *p_new_timeline;
	if(!(p_new_timeline = new(std::nothrow) TTimeLineInfo))
		return -1;
	if(!p_new_timeline->Alloc(n_buffer_length)) {
		delete p_new_timeline;
		return -1;
	}
	m_timeline_list.push_back(p_new_timeline);
	// alloc and add to the end of list

	p_new_timeline->f_min_value = f_min_value;
	p_new_timeline->f_max_value = f_max_value;
	p_new_timeline->b_normalize = b_normalize;
	p_new_timeline->v_color = v_color;
	p_new_timeline->b_solid = b_solid;
	p_new_timeline->f_opacity = f_opacity;
	// set params

	return p_new_timeline->n_id = m_n_queue_id_pool ++;
	// return id
}

/*
 *	bool CTimeLineGraph::b_IsTimeline(int n_id)
 *		- returns true if n_id specifies valid timeline. otherwise false
 */
bool CTimeLineGraph::b_IsTimeline(int n_id)
{
	return p_TimeLine(n_id) < m_timeline_list.end();
}

/*
 *	bool CTimeLineGraph::DeleteTimeline(int n_id)
 *		- deletes timeline specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::DeleteTimeline(int n_id)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		delete (*p_time_line);
		m_timeline_list.erase(p_time_line);
		return true;
	}
	return false;
}

/*
 *	void CTimeLineGraph::DeleteTimelines()
 *		- delete all timelines
 */
void CTimeLineGraph::DeleteTimelines()
{
	std::for_each(m_timeline_list.begin(), m_timeline_list.end(), CDeleteTimeline());
	m_timeline_list.clear();
}

/*
 *	bool CTimeLineGraph::AddReading(int n_id, float f_value, int n_skip = 0)
 *		- adds reading to timeline specified by n_id
 *		- f_value is reading value
 *		- n_skip is number of time frames to skip (all will be set to f_value)
 */
bool CTimeLineGraph::AddReading(int n_id, float f_value, int n_skip)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->AddReading(f_value, n_skip);
	return false;
}

template <class _Ty, class _P>
class CRefCall {
protected:
	_Ty &m_r_object;
public:
	inline CRefCall(_Ty &r_object)
		:m_r_object(r_object)
	{}

	inline void operator ()(_P param)
	{
		m_r_object(param);
	}

	inline operator bool ()const
	{
		return (bool)m_r_object;
	}
};

/*
 *	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)
 *		- render rectangular timeline window using OpenGL
 *		- p_state is OpenGL state manager
 *		- v_min and v_max are coordinates of rectangluar window
 *		- if b_allow_separate_scale is true, different timelines can be rendered with different
 *		  (maximal) scale so the values in the graph might not correspond to each other
 *		- if b_allow_separate_zero is true, different timelines can have time axis at different
 *		  vertical position
 *		- v_border_color is color for graph border and for time axes
 *		- v_back_color and f_back_opacity affect rendering of background
 */
void CTimeLineGraph::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)
{
	p_state->EnableBlend();
	p_state->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	p_state->Color4f(v_back_color.x, v_back_color.y, v_back_color.z, f_back_opacity);
	glBegin(GL_QUADS);
	glVertex2f(v_min.x, v_min.y);
	glVertex2f(v_min.x, v_max.y);
	glVertex2f(v_max.x, v_max.y);
	glVertex2f(v_max.x, v_min.y);
	glEnd();
	// fill back

	CRenderTimeLine render_timelines(p_state, b_allow_separate_scale, b_allow_separate_zero,
		v_min, v_max, f_subframe_time);

	std::for_each(m_timeline_list.begin(),
		m_timeline_list.end(), CRefCall<CRenderTimeLine, const TTimeLineInfo*>(render_timelines));
	// init

	std::for_each(m_timeline_list.begin(), m_timeline_list.end(),
		CRenderTimeLineRender(render_timelines));
	// render

	p_state->Color4f(v_border_color.x, v_border_color.y, v_border_color.z, 1);
	glBegin(GL_LINE_LOOP);
	glVertex2f(v_min.x, v_min.y);
	glVertex2f(v_min.x, v_max.y);
	glVertex2f(v_max.x, v_max.y);
	glVertex2f(v_max.x, v_min.y);
	glEnd();
	// outline back
}

/*
 *	float CTimeLineGraph::f_Min_Value(int n_id)
 *		- returns minimal value from current values in timeline window for
 *		  timeline specified by n_id
 *		- returns 0 in cas 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 CTimeLineGraph::f_Min_Value(int n_id)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->f_Cur_MinSpike();
	return false;
}

/*
 *	float CTimeLineGraph::f_Max_Value(int n_id)
 *		- returns maximal value from current values in timeline window for
 *		  timeline specified by n_id
 *		- returns 0 in cas 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 CTimeLineGraph::f_Max_Value(int n_id)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->f_Cur_MaxSpike();
	return false;
}

/*
 *	int CTimeLineGraph::n_Timeline_Num() const
 *		- returns number of timelines being currently displayed
 */
int CTimeLineGraph::n_Timeline_Num() const
{
	return int(m_timeline_list.size());
}

/*
 *	int CTimeLineGraph::n_GetTimeline_ZOrder(int n_id) const
 *		- 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>)
 *		- returns -1 in case n_id doesn't specify a valid timeline
 */
int CTimeLineGraph::n_GetTimeline_ZOrder(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return int(p_time_line - m_timeline_list.begin());
	return false;
}

/*
 *	int CTimeLineGraph::n_GetTimeline_Length(int n_id) const
 *		- returns queue length of timeline specified by n_id
 *		- returns -1 in case n_id doesn't specify a valid timeline
 */
int CTimeLineGraph::n_GetTimeline_Length(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->n_Length();
	return false;
}

/*
 *	bool CTimeLineGraph::b_GetTimeline_Normalize(int n_id) const
 *		- returns the in case timeline specified by n_id is to be drawn normalized
 *		- returns false in case n_id doesn't specify a valid timeline
 */
bool CTimeLineGraph::b_GetTimeline_Normalize(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->b_normalize;
	return false;
}

/*
 *	bool CTimeLineGraph::b_GetTimeline_Normalize(int n_id) const
 *		- returns true in case timeline specified by n_id is to be drawn solid
 *		- returns false in case n_id doesn't specify a valid timeline
 */
bool CTimeLineGraph::b_GetTimeline_Solid(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->b_solid;
	return false;
}

/*
 *	float CTimeLineGraph::f_GetTimeline_Min(int n_id) const
 *		- 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)
 *		- returns 0 in case n_id doesn't specify a valid timeline
 */
float CTimeLineGraph::f_GetTimeline_Min(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->f_min_value;
	return 0;
}

/*
 *	float CTimeLineGraph::f_GetTimeline_Max(int n_id) const
 *		- 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)
 *		- returns 0 in case n_id doesn't specify a valid timeline
 */
float CTimeLineGraph::f_GetTimeline_Max(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->f_max_value;
	return 0;
}

/*
 *	Vector3f CTimeLineGraph::v_GetTimeline_Color(int n_id) const
 *		- returns color of timeline specified by n_id
 *		- returns vector of zeros (black) in case n_id doesn't specify a valid timeline
 */
Vector3f CTimeLineGraph::v_GetTimeline_Color(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->v_color;
	return Vector3f(0, 0, 0);
}

/*
 *	float CTimeLineGraph::f_GetTimeline_Opacity(int n_id) const
 *		- returns opacity of timeline specified by n_id
 *		- returns 0 in case n_id doesn't specify a valid timeline
 */
float CTimeLineGraph::f_GetTimeline_Opacity(int n_id) const
{
	std::vector<TTimeLineInfo*>::const_iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->f_opacity;
	return 0;
}

/*
 *	bool CTimeLineGraph::SetTimeline_ZOrder(int n_id, int n_z_order)
 *		- set z-order of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_ZOrder(int n_id, int n_z_order)
{
	if(n_z_order < 0 || n_z_order >= int(m_timeline_list.size()))
		return false;
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		TTimeLineInfo *p_time_line_ptr = *p_time_line;
		m_timeline_list.erase(p_time_line);
		m_timeline_list.insert(m_timeline_list.begin() + (n_z_order - 1), p_time_line_ptr);
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Length(int n_id, int n_length)
 *		- set length of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline and there
 *		  was enough memory for new queue. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Length(int n_id, int n_length)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end())
		return (*p_time_line)->Alloc(n_length);
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Normalize(int n_id, bool b_normalize)
 *		- set normalization of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Normalize(int n_id, bool b_normalize)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->b_normalize = b_normalize;
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Solid(int n_id, bool b_solid)
 *		- set fill style of timeline, specified by n_id to b_solid
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Solid(int n_id, bool b_solid)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->b_solid = b_solid;
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Min(int n_id, float f_min)
 *		- set minimal value of graph y-axis of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Min(int n_id, float f_min)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->f_min_value = f_min;
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Max(int n_id, float f_max)
 *		- set maximal value of graph y-axis of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Max(int n_id, float f_max)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->f_max_value = f_max;
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Color(int n_id, Vector3f v_color)
 *		- set color of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Color(int n_id, Vector3f v_color)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->v_color = v_color;
		return true;
	}
	return false;
}

/*
 *	bool CTimeLineGraph::SetTimeline_Opacity(int n_id, float f_opacity)
 *		- set opacity of fill of timeline, specified by n_id
 *		- returns true if n_id specified valid timeline. otherwise false
 */
bool CTimeLineGraph::SetTimeline_Opacity(int n_id, float f_opacity)
{
	std::vector<TTimeLineInfo*>::iterator p_time_line;
	if((p_time_line = p_TimeLine(n_id)) < m_timeline_list.end()) {
		(*p_time_line)->f_opacity = f_opacity;
		return true;
	}
	return false;
}

/*
 *	inline std::vector<CTimeLineGraph::TTimeLineInfo*>::iterator
 *		CTimeLineGraph::p_TimeLine(int n_id)
 *		- returns iterator, pointing to the object with id n_id
 *		  or m_timeline_list.end() in case no such object was found in the list
 */
std::vector<CTimeLineGraph::TTimeLineInfo*>::iterator CTimeLineGraph::p_TimeLine(int n_id)
{
	return std::find_if(m_timeline_list.begin(), m_timeline_list.end(), CIfIdEquals(n_id));
}

/*
 *	inline std::vector<CTimeLineGraph::TTimeLineInfo*>::const_iterator
 *		CTimeLineGraph::p_TimeLine(int n_id) const
 *		- returns iterator, pointing to the object with id n_id
 *		  or m_timeline_list.end() in case no such object was found in the list
 */
std::vector<CTimeLineGraph::TTimeLineInfo*>::const_iterator
	CTimeLineGraph::p_TimeLine(int n_id) const
{
	return std::find_if(m_timeline_list.begin(), m_timeline_list.end(), CIfIdEquals(n_id));
}

/*
 *								=== ~CTimeLineGraph ===
 */
