/** 
 * @file llframestatview.cpp
 * @brief LLFrameStatView class implementation
 *
 * $LicenseInfo:firstyear=2001&license=viewergpl$
 * 
 * Copyright (c) 2001-2008, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llframestatview.h"

#include "llframestats.h"
#include "llrect.h"
#include "llerror.h"
#include "llgl.h"
#include "llmath.h"
#include "llfontgl.h"

#include "llui.h"
#include "llviewercontrol.h"
#include "llstat.h"


LLFrameStatView::LLFrameStatView(const std::string& name, const LLRect& rect)
:	LLView(name, rect, TRUE)
{
	setVisible(FALSE);

	mNumStats     = 0;
	mTotalTime    = 0.05f;
	mTickSpacing  = 0.01f;
	mLabelSpacing = 0.05f;
	mShowPercent  = TRUE;
	mCenterOn     = 0;
}

LLFrameStatView::~LLFrameStatView()
{
	// Children all cleaned up by default view destructor.
}

void LLFrameStatView::setup(const LLFrameStats &frame_stats)
{
	S32 i;
	for (i = 0; i < LLFrameStats::NUM_STATS; i++)
	{
		addStat(&gFrameStats.getStat(i),
				gFrameStats.getStatLabel(i),
				gFrameStats.getStatColor(i));
	}
}

BOOL LLFrameStatView::handleMouseDown(S32 x, S32 y, MASK mask)
{
	mShowPercent = !mShowPercent;
	return TRUE;
}

BOOL LLFrameStatView::handleMouseUp(S32 x, S32 y, MASK mask)
{
	return TRUE;
}


BOOL LLFrameStatView::handleHover(S32 x, S32 y, MASK mask)
{
	return FALSE;
}


void LLFrameStatView::draw()
{
	S32 i;
	S32 j;
	LLStat *statp;

	LLGLSNoTexture gls_no_texture;

	statp = mStats[0];
	if (!statp)
	{
		return;
	}
	S32 num_bins = llmin(statp->getNumValues(), (U32)32);

	F32 total_width = getRect().getWidth() - 20.f;
	F32 total_height = getRect().getHeight() - 30.f;
	S32 left, top, right, bottom;
	F32 key_height = 16;
	S32 bar_spacing = 2;
	F32 time_scale = total_width / mTotalTime;

	// Draw the window background
	gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));

	// Render the key
	left = 10;
	top = getRect().getHeight() - 10;

	BOOL is_active[MAX_STATS];
	S32 bin = mStats[0]->getCurBin() - 1;
	if (bin < 0)
	{
		bin += mStats[0]->getNumBins();
	}

	F64 min_last_time = mStats[0]->getBinTime(bin);

	for (i = 0; i < mNumStats; i++)
	{
		if (mStats[i]->getLastTime() >= min_last_time)
		{
			is_active[i] = TRUE;
		}
		else
		{
			is_active[i] = FALSE;
		}
	}

	S32 cur_center = mCenterOn;
	if (!is_active[mCenterOn])
	{
		for (i = 0; i < mNumStats; i++)
		{
			if (is_active[i])
			{
				cur_center = i;
				break;
			}
		}
	}


	for (i = 0; i < mNumStats; i++)
	{
		// Draw the color key.
		LLGLSNoTexture gls_no_texture;
		gl_rect_2d(left, top - 2, left + 10, top - 12, mColors[i]);

		left = left + 15;
		// Draw the label.
		LLFontGL::sMonospace->renderUTF8(mLabels[i], 0, left, top,
			LLColor4(1.f, 1.f, 1.f, 1.f),
			LLFontGL::LEFT, LLFontGL::TOP);
	
		left = left + LLFontGL::sMonospace->getWidth(mLabels[i]) + 10;
		if ((i + 1) < mNumStats)
		{
			if ((left + LLFontGL::sMonospace->getWidth(mLabels[i+1])) > (getRect().getWidth() - 10))
			{
				left = 10;
				top -= llfloor(LLFontGL::sMonospace->getLineHeight());
				key_height += LLFontGL::sMonospace->getLineHeight();
			}
		}
	}

	top -= llfloor(LLFontGL::sMonospace->getLineHeight());
	bottom = top - llfloor(LLFontGL::sMonospace->getLineHeight());
	F32 total_time = 0.f;
	F32 bin_means[32];

	key_height += 20;

	// Draw the "Average" bar
	
	// Draw ticks for the "Average bar"
	F32 tick_value;
	char tick_label[256];		/* Flawfinder: ignore */
	for (tick_value = 0; tick_value <= 100; tick_value += 10)
	{
		left = 10 + llfloor(tick_value*(total_width/100.f));
		right = left + 1;
		gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.2f));
	}

	// Draw the tick labels (and big ticks).
	bottom = bottom - 4;
	for (tick_value = 0; tick_value <= 100; tick_value += 20)
	{
		LLGLSNoTexture gls_no_texture;
		left = 10 + llfloor(tick_value*(total_width/100.f));
		right = left + 1;
		gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.2f));

		snprintf(tick_label, sizeof(tick_label), "%.2f", tick_value);			/* Flawfinder: ignore */
		// draw labels for the tick marks
		LLFontGL::sMonospace->renderUTF8(tick_label, 0, left, bottom,
			LLColor4(1.f, 1.f, 1.f, 0.5f),
			LLFontGL::LEFT, LLFontGL::TOP);
	}

	for (i = 0; i < mNumStats; i++)
	{
		bin_means[i] = mStats[i]->getMeanDuration();
		total_time += bin_means[i];
	}

	F32 time_frac = total_width / total_time;
	F32 cur_left = .0f;
	for (i = 0; i < mNumStats; i++)
	{
		F32 cur_right = cur_left + bin_means[i] * time_frac;
		left = llfloor(cur_left) + 10;
		right = llfloor(cur_right) + 10;
		gl_rect_2d(left, top, right, bottom, mColors[i]);
		cur_left = cur_right;
	}

	S32 bar_height = llmax(1, (S32)((total_height - bar_spacing*(num_bins-1) - key_height) / num_bins));
	S32 graph_height = bar_height * num_bins + bar_spacing * num_bins - 1;

	// Draw the tick marks.
	top = getRect().getHeight() - llfloor(key_height) - 20;

	bottom = top - graph_height - 2;
	// Render the ticks and labels
	if (mShowPercent)
	{
		for (tick_value = 0; tick_value <= 100; tick_value += 10)
		{
			left = 10 + llfloor(tick_value*(total_width/100.f));
			right = left + 1;
			gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.1f));
		}

		// Draw the tick labels (and big ticks).
		bottom = top - graph_height - 4;
		for (tick_value = 0; tick_value <= 100; tick_value += 20)
		{
			LLGLSNoTexture gls_no_texture;
			left = 10 + llfloor(tick_value*(total_width/100.f));
			right = left + 1;
			gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.25f));

			snprintf(tick_label, sizeof(tick_label), "%.2f", tick_value);			/* Flawfinder: ignore */
			// draw labels for the tick marks
			LLFontGL::sMonospace->renderUTF8(tick_label, 0, left, bottom,
				LLColor4(1.f, 1.f, 1.f, 0.5f),
				LLFontGL::LEFT, LLFontGL::TOP);
		}
	}
	else
	{
		for (tick_value = 0; tick_value <= mTotalTime; tick_value += mTickSpacing)
		{
			left = 10 + llfloor(tick_value*time_scale);
			right = left + 1;
			gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.1f));
		}

		// Draw the tick labels (and big ticks).
		bottom = top - graph_height - 4;
		for (tick_value = 0; tick_value <= mTotalTime; tick_value += mLabelSpacing)
		{
			LLGLSNoTexture gls_no_texture;
			left = 10 + llfloor(tick_value*time_scale);
			right = left + 1;
			gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.25f));

			snprintf(tick_label, sizeof(tick_label), "%.2f", tick_value);			/* Flawfinder: ignore */
			// draw labels for the tick marks
			LLFontGL::sMonospace->renderUTF8(tick_label, 0, left, bottom,
				LLColor4(1.f, 1.f, 1.f, 0.5f),
				LLFontGL::LEFT, LLFontGL::TOP);
		}
	}

	// Draw the graph background
	bottom = top - graph_height;
	left = 10;
	right = left + llfloor(total_width);

	gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.25f));

	F64 min_time = 0.f;
	F64 max_time = 0.f;

	// Determine which stats are active.

	top = getRect().getHeight() - llfloor(key_height) - 20;
	bottom = top - bar_height;
	for (i = 0; i < 32; i++)
	{
		// Find the min time for this set of bins.
		min_time = mStats[0]->getPrevBeginTime(i);
		max_time = mStats[0]->getPrevBeginTime(i);
		for (j = 0; j < mNumStats; j++)
		{
			if (is_active[j])
			{
				min_time = llmin(min_time, mStats[j]->getPrevBeginTime(i));
				max_time = llmax(max_time, mStats[j]->getPrevTime(i));
			}
		}

		if (mShowPercent)
		{
			for (j = 0; j < mNumStats; j++)
			{
				if (is_active[j])
				{
					statp = mStats[j];
					F64 begin = statp->getPrevBeginTime(i) - min_time;
					F64 end = statp->getPrevTime(i) - min_time;

					F32 begin_frac = (F32)(begin/(max_time - min_time));
					F32 end_frac   = (F32)(end/(max_time - min_time));

					left = llfloor(begin_frac * total_width) + 10;
					right = llfloor(end_frac * total_width) + 10;
					gl_rect_2d(left, top, right, bottom, mColors[j]);
				}
			}
		}
		else
		{
			F64 center_time = mStats[cur_center]->getPrevBeginTime(i);
			center_time = min_time;

			// Now, draw the bars for each stat
			for (j = 0; j < mNumStats; j++)
			{
				if (is_active[j])
				{
					statp = mStats[j];
					F64 begin = statp->getPrevBeginTime(i);
					F64 end   = statp->getPrevTime(i);

					F32 begin_frac = (F32)(begin - center_time);
					F32 end_frac   = (F32)(end - center_time);

					left  = llfloor(begin_frac * time_scale) + 10;
					right = llfloor(end_frac * time_scale) + 10;

					gl_rect_2d(left, top, right, bottom, mColors[j]);
				}
			}
		}
		top = top - bar_height - bar_spacing;
		bottom = top - bar_height;
	}
	LLView::draw();
}

void LLFrameStatView::addStat(LLStat *statp, const char *label, const LLColor4 &color)
{
	if( mNumStats >= MAX_STATS )
	{
		llwarns << "LLFrameStatView::addStat - too many stats!" << llendl;
		return;
	}

	mStats[mNumStats] = statp;
	mColors[mNumStats] = color;
	mLabels[mNumStats] = label;
	mNumStats++;
}