//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "vgui_basebudgetpanel.h"
#include "vgui_controls/Label.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

CBaseBudgetPanel::CBaseBudgetPanel( vgui::Panel *pParent, const char *pElementName )
	 :	vgui::Panel( pParent, pElementName )
{
	m_BudgetHistoryOffset = 0;

	SetProportional( false );
	SetKeyBoardInputEnabled( false );
	SetMouseInputEnabled( false );
	SetVisible( true );

	// set the scheme before any child control is created
	vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "Client");
	SetScheme( scheme );

	m_pBudgetHistoryPanel = NULL;
	m_pBudgetBarGraphPanel = NULL;
	SetZPos( 1001 );

	m_bDedicated = false;
}


CBaseBudgetPanel::~CBaseBudgetPanel()
{
}


float CBaseBudgetPanel::GetBudgetGroupPercent( float value )
{
	if ( m_ConfigData.m_flBarGraphRange == 0.0f )
		return 1.0f;
	return value / m_ConfigData.m_flBarGraphRange;
}


const double *CBaseBudgetPanel::GetBudgetGroupData( int &nGroups, int &nSamplesPerGroup, int &nSampleOffset ) const
{
	nGroups = m_ConfigData.m_BudgetGroupInfo.Count();
	nSamplesPerGroup = BUDGET_HISTORY_COUNT;
	nSampleOffset = m_BudgetHistoryOffset;
	if( m_BudgetGroupTimes.Count() == 0 )
	{
		return NULL;
	}
	else
	{
		return &m_BudgetGroupTimes[0].m_Time[0];
	}
}


void CBaseBudgetPanel::ClearTimesForAllGroupsForThisFrame( void )
{
	int i;
	for( i = 0; i < m_ConfigData.m_BudgetGroupInfo.Count(); i++ )
	{
		m_BudgetGroupTimes[i].m_Time[m_BudgetHistoryOffset] = 0.0;
	}
}

void CBaseBudgetPanel::ClearAllTimesForGroup( int groupID )
{
	int i;
	for( i = 0; i < BUDGET_HISTORY_COUNT; i++ )
	{
		m_BudgetGroupTimes[groupID].m_Time[i] = 0.0;
	}
}


void CBaseBudgetPanel::OnConfigDataChanged( const CBudgetPanelConfigData &data )
{
	int oldNumGroups = m_ConfigData.m_BudgetGroupInfo.Count();

	// Copy in the config data and rebuild everything.
	Rebuild( data );
	
	if ( m_ConfigData.m_BudgetGroupInfo.Count() > m_BudgetGroupTimes.Count() )
	{
		m_BudgetGroupTimes.EnsureCount( m_ConfigData.m_BudgetGroupInfo.Count() );
		for ( int i = oldNumGroups; i < m_ConfigData.m_BudgetGroupInfo.Count(); i++ )
		{
			ClearAllTimesForGroup( i );
		}
	}
	else
	{
		m_BudgetGroupTimes.SetSize( m_ConfigData.m_BudgetGroupInfo.Count() );
		for ( int i = 0; i < m_BudgetGroupTimes.Count(); i++ )
		{
			ClearAllTimesForGroup( i );
		}
	}

	InvalidateLayout( false, true );
}


void CBaseBudgetPanel::ResetAll()
{
	m_ConfigData.m_BudgetGroupInfo.Purge();
	
	for ( int i=0; i < m_GraphLabels.Count(); i++ )
		m_GraphLabels[i]->MarkForDeletion();
	m_GraphLabels.Purge();

	for ( int i=0; i < m_TimeLabels.Count(); i++ )
		m_TimeLabels[i]->MarkForDeletion();
	m_TimeLabels.Purge();
}


void CBaseBudgetPanel::Rebuild( const CBudgetPanelConfigData &data )
{
	int oldNumBudgetGroups = m_ConfigData.m_BudgetGroupInfo.Count();
	int oldNumHistoryLabels = m_ConfigData.m_HistoryLabelValues.Count();
	
	int oldNumTimeLabels = m_TimeLabels.Count();

	// Copy the new config in.
	m_ConfigData = data;

	int nParentWidth, nParentHeight;
	GetParent()->GetSize( nParentWidth, nParentHeight );
	if ( m_ConfigData.m_Width > nParentWidth )
	{
		m_ConfigData.m_Width = nParentWidth;
	}
	if ( m_ConfigData.m_Height > nParentHeight )
	{
		m_ConfigData.m_Height = nParentHeight;
	}
	if ( m_ConfigData.m_xCoord + m_ConfigData.m_Width > nParentWidth )
	{
		m_ConfigData.m_xCoord = nParentWidth - m_ConfigData.m_Width;
	}
	if ( m_ConfigData.m_yCoord + m_ConfigData.m_Height > nParentHeight )
	{
		m_ConfigData.m_yCoord = nParentHeight - m_ConfigData.m_Height;
	}

	// Recreate the history and bar graph panels.
	if( m_pBudgetHistoryPanel )
	{
		m_pBudgetHistoryPanel->MarkForDeletion();
	}
	m_pBudgetHistoryPanel = new CBudgetHistoryPanel( this, "FrametimeHistory" );

	if( m_pBudgetBarGraphPanel )
	{
		m_pBudgetBarGraphPanel->MarkForDeletion();
	}
	m_pBudgetBarGraphPanel = new CBudgetBarGraphPanel( this, "BudgetBarGraph" );

	// Create any new labels we need.
	int i;

	if ( m_ConfigData.m_BudgetGroupInfo.Count() > m_GraphLabels.Count() )
	{
		m_GraphLabels.EnsureCount( m_ConfigData.m_BudgetGroupInfo.Count() );
		for( i = oldNumBudgetGroups; i < m_ConfigData.m_BudgetGroupInfo.Count(); i++ )
		{
			const char *pBudgetGroupName = m_ConfigData.m_BudgetGroupInfo[i].m_Name.String();
			m_GraphLabels[i] = new vgui::Label( this, pBudgetGroupName, pBudgetGroupName );
		}
	}
	else
	{
		while ( m_GraphLabels.Count() > m_ConfigData.m_BudgetGroupInfo.Count() )
		{
			m_GraphLabels[m_GraphLabels.Count()-1]->MarkForDeletion();
			m_GraphLabels.Remove( m_GraphLabels.Count()-1 );
		}
	}
	Assert( m_GraphLabels.Count() == m_ConfigData.m_BudgetGroupInfo.Count() );


	// Create new history labels.
	if ( m_ConfigData.m_HistoryLabelValues.Count() > m_HistoryLabels.Count() )
	{
		m_HistoryLabels.EnsureCount( m_ConfigData.m_HistoryLabelValues.Count() );
		for ( i=oldNumHistoryLabels; i < m_HistoryLabels.Count(); i++ )
		{
			m_HistoryLabels[i] = new vgui::Label( this, "history label", "history label" );
		}
	}
	else
	{
		while ( m_HistoryLabels.Count() > m_ConfigData.m_HistoryLabelValues.Count() )
		{
			m_HistoryLabels[m_HistoryLabels.Count()-1]->MarkForDeletion();
			m_HistoryLabels.Remove( m_HistoryLabels.Count()-1 );
		}
	}
	SetHistoryLabelText();

	
	// Note: the time lines still use milliseconds for the computations about where to draw them,
	// but each BudgetGroupDataType_t has its own scale.
	int nTimeLabels = m_ConfigData.m_flBarGraphRange + data.m_flTimeLabelInterval;
	if ( data.m_flTimeLabelInterval != 0.0f )
	{
		nTimeLabels /= data.m_flTimeLabelInterval;
	}

	if ( nTimeLabels > m_TimeLabels.Count() )
	{
		m_TimeLabels.EnsureCount( nTimeLabels );
		for( i = oldNumTimeLabels; i < m_TimeLabels.Count(); i++ )
		{
			char name[1024];
			Q_snprintf( name, sizeof( name ), "time_label_%d", i );
			m_TimeLabels[i] = new vgui::Label( this, name, "TEXT NOT SET YET" );
		}
	}
	else
	{
		while ( m_TimeLabels.Count() > nTimeLabels )
		{
			m_TimeLabels[m_TimeLabels.Count()-1]->MarkForDeletion();
			m_TimeLabels.Remove( m_TimeLabels.Count()-1 );
		}
	}

	SetTimeLabelText();
}

void CBaseBudgetPanel::UpdateWindowGeometry()
{
	if( m_ConfigData.m_Width > BUDGET_HISTORY_COUNT )
	{
		m_ConfigData.m_Width = BUDGET_HISTORY_COUNT;
	}

	SetPos( m_ConfigData.m_xCoord, m_ConfigData.m_yCoord );
	SetSize( m_ConfigData.m_Width, m_ConfigData.m_Height );
}

void CBaseBudgetPanel::PerformLayout()
{
	if ( !m_pBudgetHistoryPanel || !m_pBudgetBarGraphPanel )
		return;


	int maxFPSLabelWidth = 0;
	int i;
	for( i = 0; i < m_HistoryLabels.Count(); i++ )
	{
		int labelWidth, labelHeight;
		m_HistoryLabels[i]->GetContentSize( labelWidth, labelHeight );
		if( labelWidth > maxFPSLabelWidth )
		{
			maxFPSLabelWidth = labelWidth;
		}
	}

	m_pBudgetHistoryPanel->SetRange( 0, m_ConfigData.m_flHistoryRange );

	
	float bottomOfHistoryPercentage = m_ConfigData.m_flBottomOfHistoryFraction;
	UpdateWindowGeometry();
	int x, y, totalWidth, totalHeight;
	int totalHeightMinusTimeLabels;
	GetPos( x, y );
	GetSize( totalWidth, totalHeight );

	int maxTimeLabelHeight = 0;
	for( i = 0; i < m_TimeLabels.Count(); i++ )
	{
		int labelWidth, labelHeight;
		m_TimeLabels[i]->GetContentSize( labelWidth, labelHeight );
		maxTimeLabelHeight = max( maxTimeLabelHeight, labelHeight );
	}

	totalHeightMinusTimeLabels = totalHeight - maxTimeLabelHeight;
	
	m_pBudgetHistoryPanel->SetPos( 0, 0 );
	int budgetHistoryHeight = totalHeightMinusTimeLabels * bottomOfHistoryPercentage;
	m_pBudgetHistoryPanel->SetSize( totalWidth - maxFPSLabelWidth, 
		budgetHistoryHeight );

	int maxLabelWidth = 0;
	for( i = 0; i < m_GraphLabels.Count(); i++ )
	{
		int width, height;
		m_GraphLabels[i]->GetContentSize( width, height );
		if( maxLabelWidth < width )
		{
			maxLabelWidth = width;
		}
	}
	
	m_pBudgetBarGraphPanel->SetPos( maxLabelWidth, 
		totalHeightMinusTimeLabels * bottomOfHistoryPercentage );
	m_pBudgetBarGraphPanel->SetSize( totalWidth - maxLabelWidth, 
		totalHeightMinusTimeLabels * ( 1 - bottomOfHistoryPercentage ) );

	for( i = 0; i < m_GraphLabels.Count(); i++ )
	{
		m_GraphLabels[i]->SetPos( 0, 
			( bottomOfHistoryPercentage * totalHeightMinusTimeLabels ) +
			( i * totalHeightMinusTimeLabels * 
			( 1 - bottomOfHistoryPercentage ) ) / m_ConfigData.m_BudgetGroupInfo.Count() );
		// fudge height by 1 for rounding 
		m_GraphLabels[i]->SetSize( maxLabelWidth, 1 + ( totalHeightMinusTimeLabels * 
			( 1 - bottomOfHistoryPercentage ) ) / m_ConfigData.m_BudgetGroupInfo.Count() );
		m_GraphLabels[i]->SetContentAlignment( vgui::Label::a_east );
	}

	// Note: the time lines still use milliseconds for the computations about where to draw them,
	// but each BudgetGroupDataType_t has its own scale.
	float fRange = m_ConfigData.m_flBarGraphRange;
	for( i = 0; i < m_TimeLabels.Count(); i++ )
	{
		int labelWidth, labelHeight;
		m_TimeLabels[i]->GetContentSize( labelWidth, labelHeight );
		x = maxLabelWidth + ( i * m_ConfigData.m_flTimeLabelInterval ) / fRange * ( totalWidth - maxLabelWidth );
		
		m_TimeLabels[i]->SetPos( x - ( labelWidth * 0.5 ), totalHeight - labelHeight );
		m_TimeLabels[i]->SetSize( labelWidth, labelHeight );
		m_TimeLabels[i]->SetContentAlignment( vgui::Label::a_east );
	}


	// position the fps labels
	fRange = m_ConfigData.m_flHistoryRange;
	for( i = 0; i < m_HistoryLabels.Count(); i++ )
	{
		int labelWidth, labelHeight;
		m_HistoryLabels[i]->GetContentSize( labelWidth, labelHeight );
		y = (fRange != 0) ? budgetHistoryHeight * m_ConfigData.m_HistoryLabelValues[i] / ( float )fRange : 0.0f;
		int top = ( int )( budgetHistoryHeight - y - 1 - labelHeight * 0.5f );
		m_HistoryLabels[i]->SetPos( totalWidth - maxFPSLabelWidth, top );
		m_HistoryLabels[i]->SetSize( labelWidth, labelHeight );
		m_HistoryLabels[i]->SetContentAlignment( vgui::Label::a_east );
	}
}

void CBaseBudgetPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	int i;
	for( i = 0; i < m_ConfigData.m_BudgetGroupInfo.Count(); i++ )
	{
		m_GraphLabels[i]->SetFgColor( m_ConfigData.m_BudgetGroupInfo[i].m_Color );
		m_GraphLabels[i]->SetBgColor( Color( 0, 0, 0, 255 ) );
		m_GraphLabels[i]->SetPaintBackgroundEnabled( false );
		m_GraphLabels[i]->SetFont( pScheme->GetFont( "BudgetLabel", IsProportional() ) );
		if ( m_bDedicated )
		{
			m_GraphLabels[i]->SetBgColor( pScheme->GetColor( "ControlBG", Color( 0, 0, 0, 255 ) ) );
		}
	}

	for( i = 0; i < m_TimeLabels.Count(); i++ )
	{
		int red, green, blue, alpha;
		red = green = blue = alpha = 255;
		m_TimeLabels[i]->SetFgColor( Color( red, green, blue, alpha ) );
		m_TimeLabels[i]->SetBgColor( Color( 0, 0, 0, 255 ) );
		m_TimeLabels[i]->SetPaintBackgroundEnabled( false );
		m_TimeLabels[i]->SetFont( pScheme->GetFont( "BudgetLabel", IsProportional() ) );
		if ( m_bDedicated )
		{
			m_TimeLabels[i]->SetBgColor( pScheme->GetColor( "ControlBG", Color( 0, 0, 0, 255 ) ) );
		}
	}

	for( i = 0; i < m_HistoryLabels.Count(); i++ )
	{
		int red, green, blue, alpha;
		red = green = blue = alpha = 255;
		m_HistoryLabels[i]->SetFgColor( Color( red, green, blue, alpha ) );
		m_HistoryLabels[i]->SetBgColor( Color( 0, 0, 0, 255 ) );
		m_HistoryLabels[i]->SetPaintBackgroundEnabled( false );
		m_HistoryLabels[i]->SetFont( pScheme->GetFont( "BudgetLabel", IsProportional() ) );
		if ( m_bDedicated )
		{
			m_HistoryLabels[i]->SetBgColor( pScheme->GetColor( "ControlBG", Color( 0, 0, 0, 255 ) ) );
		}
	}

	m_hFont = pScheme->GetFont( "DefaultFixed" );

	if ( m_bDedicated )
	{
		SetBgColor( pScheme->GetColor( "ControlBG", Color( 0, 0, 0, 255 ) ) );
	}
	SetPaintBackgroundEnabled( true );
}

void CBaseBudgetPanel::PaintBackground()
{
	if ( m_bDedicated )
	{
		m_pBudgetBarGraphPanel->SetBgColor( GetBgColor() );
	}
	else
	{
		SetBgColor( Color( 0, 0, 0, m_ConfigData.m_flBackgroundAlpha ) );
	}
	BaseClass::PaintBackground();
}

void CBaseBudgetPanel::Paint()
{
	m_pBudgetHistoryPanel->SetData( &m_BudgetGroupTimes[0].m_Time[0], GetNumCachedBudgetGroups(), BUDGET_HISTORY_COUNT, m_BudgetHistoryOffset );
	BaseClass::Paint();
}

void CBaseBudgetPanel::MarkForFullRepaint()
{
	Repaint();
	m_pBudgetHistoryPanel->Repaint();
	m_pBudgetBarGraphPanel->Repaint();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseBudgetPanel::GetGraphLabelScreenSpaceTopAndBottom( int id, int &top, int &bottom )
{
	int x = 0;
	int y = 0;
	m_GraphLabels[id]->LocalToScreen( x, y );
	top = y;
	bottom = top + m_GraphLabels[id]->GetTall();
}