//=========== (C) Copyright 1996-2002 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: VGUI scoreboard
//
// $Workfile:     $
// $Date:         $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================


#include<VGUI_LineBorder.h>

#include "hud.h"
#include "cl_util.h"
#include "const.h"
#include "entity_state.h"
#include "cl_entity.h"
#include "vgui_TeamFortressViewport.h"
#include "vgui_ScorePanel.h"
#include "vgui_helpers.h"
#include "vgui_loadtga.h"
#include "voice_status.h"
#include "vgui_SpectatorPanel.h"

int HUD_IsGame( const char *game );
int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 );

// Scoreboard dimensions
#define SBOARD_TITLE_SIZE_Y			YRES(22)

#define X_BORDER					XRES(4)

// Column sizes
class SBColumnInfo
{
public:
	const char		*m_pTitle;		// If null, ignore, if starts with #, it's localized, otherwise use the string directly.
	int			 m_Width;		// Based on 640 width. Scaled to fit other resolutions.
	Label::Alignment	 m_Alignment;	
};

// grid size is marked out for 640x480 screen

SBColumnInfo g_ColumnInfo[NUM_COLUMNS] =
{
	{NULL,			24,			Label::a_east},		// tracker column
	{NULL,			140,		Label::a_east},		// name
	{NULL,			56,			Label::a_east},		// class
	{"#SCORE",		40,			Label::a_east},
	{"#DEATHS",		46,			Label::a_east},
	{"#LATENCY",	46,			Label::a_east},
	{"#VOICE",		40,			Label::a_east},
	{NULL,			2,			Label::a_east},		// blank column to take up the slack
};


#define TEAM_NO				0
#define TEAM_YES			1
#define TEAM_SPECTATORS		2
#define TEAM_BLANK			3


//-----------------------------------------------------------------------------
// ScorePanel::HitTestPanel.
//-----------------------------------------------------------------------------

void ScorePanel::HitTestPanel::internalMousePressed( MouseCode code )
{
	for( int i = 0; i < _inputSignalDar.getCount(); i++ )
	{
		_inputSignalDar[i]->mousePressed( code, this );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create the ScoreBoard panel
//-----------------------------------------------------------------------------
ScorePanel::ScorePanel( int x, int y, int wide, int tall ) : Panel( x, y, wide, tall )
{
	CSchemeManager *pSchemes = gViewPort->GetSchemeManager();
	SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle( "Scoreboard Title Text" );
	SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle( "Scoreboard Small Text" );
	Font *tfont = pSchemes->getFont( hTitleScheme );
	Font *smallfont = pSchemes->getFont( hSmallScheme );

	setBgColor( 0, 0, 0, 96 );
	m_pCurrentHighlightLabel = NULL;
	m_iHighlightRow = -1;

	//m_pTrackerIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardtracker.tga");

	// Initialize the top title.
	m_TitleLabel.setFont( tfont );
	m_TitleLabel.setText( "" );
	m_TitleLabel.setBgColor( 0, 0, 0, 255 );
	m_TitleLabel.setFgColor( Scheme::sc_primary1 );
	m_TitleLabel.setContentAlignment( vgui::Label::a_west );

	LineBorder *border = new LineBorder( Color( 60, 60, 60, 128 ) );
	setBorder( border );
	setPaintBorderEnabled( true );

	int xpos = g_ColumnInfo[0].m_Width + 3;
	if( ScreenWidth >= 640 )
	{
		// only expand column size for res greater than 640
		xpos = XRES( xpos );
	}
	m_TitleLabel.setBounds( xpos, 4, wide, SBOARD_TITLE_SIZE_Y );
	m_TitleLabel.setContentFitted( false );
	m_TitleLabel.setParent( this );

	// Setup the header (labels like "name", "class", etc..).
	m_HeaderGrid.SetDimensions( NUM_COLUMNS, 1 );
	m_HeaderGrid.SetSpacing( 0, 0 );
	
	for( int i = 0; i < NUM_COLUMNS; i++ )
	{
		if( g_ColumnInfo[i].m_pTitle && g_ColumnInfo[i].m_pTitle[0] == '#' )
			m_HeaderLabels[i].setText( CHudTextMessage::BufferedLocaliseTextString( g_ColumnInfo[i].m_pTitle ) );
		else if( g_ColumnInfo[i].m_pTitle )
			m_HeaderLabels[i].setText( g_ColumnInfo[i].m_pTitle );

		int xwide = g_ColumnInfo[i].m_Width;
		if( ScreenWidth >= 640 )
		{
			xwide = XRES( xwide );
		}
		else if( ScreenWidth == 400 )
		{
			// hack to make 400x300 resolution scoreboard fit
			if( i == 1 )
			{
				// reduces size of player name cell
				xwide -= 28;
			}
			else if( i == 0 )
			{
				// tracker icon cell
				xwide -= 8;
			}
		}
		
		m_HeaderGrid.SetColumnWidth( i, xwide );
		m_HeaderGrid.SetEntry( i, 0, &m_HeaderLabels[i] );

		m_HeaderLabels[i].setBgColor( 0, 0, 0, 255 );
		m_HeaderLabels[i].setFgColor( Scheme::sc_primary1 );
		m_HeaderLabels[i].setFont( smallfont );
		m_HeaderLabels[i].setContentAlignment( g_ColumnInfo[i].m_Alignment );

		int yres = 12;
		if( ScreenHeight >= 480 )
		{
			yres = YRES( yres );
		}
		m_HeaderLabels[i].setSize( 50, yres );
	}

	// Set the width of the last column to be the remaining space.
	int ex, ey, ew, eh;

	m_HeaderGrid.GetEntryBox( NUM_COLUMNS - 2, 0, ex, ey, ew, eh );
	m_HeaderGrid.SetColumnWidth( NUM_COLUMNS - 1, ( wide - X_BORDER ) - ( ex + ew ) );

	m_HeaderGrid.AutoSetRowHeights();
	m_HeaderGrid.setBounds( X_BORDER, SBOARD_TITLE_SIZE_Y, wide - X_BORDER * 2, m_HeaderGrid.GetRowHeight( 0 ) );
	m_HeaderGrid.setParent( this );
	m_HeaderGrid.setBgColor( 0, 0, 0, 255 );

	// Now setup the listbox with the actual player data in it.
	int headerX, headerY, headerWidth, headerHeight;

	m_HeaderGrid.getBounds( headerX, headerY, headerWidth, headerHeight );
	m_PlayerList.setBounds( headerX, headerY+headerHeight, headerWidth, tall - headerY - headerHeight - 6 );
	m_PlayerList.setBgColor( 0, 0, 0, 255 );
	m_PlayerList.setParent( this );

	for( int row=0; row < NUM_ROWS; row++ )
	{
		CGrid *pGridRow = &m_PlayerGrids[row];

		pGridRow->SetDimensions( NUM_COLUMNS, 1 );

		for( int col = 0; col < NUM_COLUMNS; col++ )
		{
			m_PlayerEntries[col][row].setContentFitted( false );
			m_PlayerEntries[col][row].setRow( row );
			m_PlayerEntries[col][row].addInputSignal( this );
			pGridRow->SetEntry(col, 0, &m_PlayerEntries[col][row]);
		}

		pGridRow->setBgColor( 0, 0, 0, 255 );
		pGridRow->SetSpacing( 0, 0 );
		pGridRow->CopyColumnWidths( &m_HeaderGrid );
		pGridRow->AutoSetRowHeights();
		pGridRow->setSize( PanelWidth( pGridRow ), pGridRow->CalcDrawHeight() );
		pGridRow->RepositionContents();

		m_PlayerList.AddItem( pGridRow );
	}

	// Add the hit test panel. It is invisible and traps mouse clicks so we can go into squelch mode.
	m_HitTestPanel.setBgColor( 0, 0, 0, 255 );
	m_HitTestPanel.setParent( this );
	m_HitTestPanel.setBounds( 0, 0, wide, tall );
	m_HitTestPanel.addInputSignal( this );

	m_pCloseButton = new CommandButton( "x", wide - XRES( 12 + 4 ), YRES( 2 ), XRES( 12 ) , YRES( 12 ) );
	m_pCloseButton->setParent( this );
	m_pCloseButton->addActionSignal( new CMenuHandler_StringCommandWatch( "-showscores", true ) );
	m_pCloseButton->setBgColor( 0, 0, 0, 255 );
	m_pCloseButton->setFgColor( 255, 255, 255, 0 );
	m_pCloseButton->setFont( tfont );
	m_pCloseButton->setBoundKey( (char)255 );
	m_pCloseButton->setContentAlignment( Label::a_center );

	Initialize();
}

//-----------------------------------------------------------------------------
// Purpose: Called each time a new level is started.
//-----------------------------------------------------------------------------
void ScorePanel::Initialize( void )
{
	// Clear out scoreboard data
	m_iLastKilledBy = 0;
	m_fLastKillTime = 0;
	m_iPlayerNum = 0;
	m_iNumTeams = 0;
	memset( g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo );
	memset( g_TeamInfo, 0, sizeof g_TeamInfo );
}

bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] )
{
	return !!gEngfuncs.GetPlayerUniqueID( iPlayer, playerID ); // TODO remove after testing
}
		
//-----------------------------------------------------------------------------
// Purpose: Recalculate the internal scoreboard data
//-----------------------------------------------------------------------------
void ScorePanel::Update()
{
	int i;

	// Set the title
	if( gViewPort->m_szServerName[0] != '\0' )
	{
		m_TitleLabel.setText( gViewPort->m_szServerName );
	}

	m_iRows = 0;
	gViewPort->GetAllPlayersInfo();

	// Clear out sorts
	for (i = 0; i < NUM_ROWS; i++)
	{
		m_iSortedRows[i] = 0;
		m_iIsATeam[i] = TEAM_NO;
	}
	for (i = 0; i < MAX_PLAYERS; i++)
	{
		m_bHasBeenSorted[i] = false;
	}

	// If it's not teamplay, sort all the players. Otherwise, sort the teams.
	if( !gHUD.m_Teamplay )
		SortPlayers( 0, NULL );
	else
		SortTeams();

	// set scrollbar range
	m_PlayerList.SetScrollRange(m_iRows);

	FillGrid();

	if( gViewPort->m_pSpectatorPanel->m_menuVisible )
	{
		 m_pCloseButton->setVisible ( true );
	}
	else 
	{
		 m_pCloseButton->setVisible ( false );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Sort all the teams
//-----------------------------------------------------------------------------
void ScorePanel::SortTeams()
{
	// clear out team scores
	int i;
	for ( i = 1; i <= m_iNumTeams; i++ )
	{
		if( !g_TeamInfo[i].scores_overriden )
			g_TeamInfo[i].frags = g_TeamInfo[i].deaths = 0;
		g_TeamInfo[i].ping = g_TeamInfo[i].packetloss = 0;
	}

	// recalc the team scores, then draw them
	for( i = 1; i < MAX_PLAYERS; i++ )
	{
		if( g_PlayerInfoList[i].name == 0 )
			continue; // empty player slot, skip

		if( g_PlayerExtraInfo[i].teamname[0] == 0 )
			continue; // skip over players who are not in a team

		// find what team this player is in
		int j;
		for ( j = 1; j <= m_iNumTeams; j++ )
		{
			if( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) )
				break;
		}
		if( j > m_iNumTeams )  // player is not in a team, skip to the next guy
			continue;

		if( !g_TeamInfo[j].scores_overriden )
		{
			g_TeamInfo[j].frags += g_PlayerExtraInfo[i].frags;
			g_TeamInfo[j].deaths += g_PlayerExtraInfo[i].deaths;
		}

		g_TeamInfo[j].ping += g_PlayerInfoList[i].ping;
		g_TeamInfo[j].packetloss += g_PlayerInfoList[i].packetloss;

		if( g_PlayerInfoList[i].thisplayer )
			g_TeamInfo[j].ownteam = TRUE;
		else
			g_TeamInfo[j].ownteam = FALSE;

		// Set the team's number (used for team colors)
		g_TeamInfo[j].teamnumber = g_PlayerExtraInfo[i].teamnumber;
	}

	// find team ping/packetloss averages
	for( i = 1; i <= m_iNumTeams; i++ )
	{
		g_TeamInfo[i].already_drawn = FALSE;

		if( g_TeamInfo[i].players > 0 )
		{
			g_TeamInfo[i].ping /= g_TeamInfo[i].players;  // use the average ping of all the players in the team as the teams ping
			g_TeamInfo[i].packetloss /= g_TeamInfo[i].players;  // use the average ping of all the players in the team as the teams ping
		}
	}

	// Draw the teams
	while( 1 )
	{
		int highest_frags = -99999; int lowest_deaths = 99999;
		int best_team = 0;

		for( i = 1; i <= m_iNumTeams; i++ )
		{
			if( g_TeamInfo[i].players < 1 )
				continue;

			if( !g_TeamInfo[i].already_drawn && g_TeamInfo[i].frags >= highest_frags )
			{
				if( g_TeamInfo[i].frags > highest_frags || g_TeamInfo[i].deaths < lowest_deaths )
				{
					best_team = i;
					lowest_deaths = g_TeamInfo[i].deaths;
					highest_frags = g_TeamInfo[i].frags;
				}
			}
		}

		// draw the best team on the scoreboard
		if( !best_team )
			break;

		// Put this team in the sorted list
		m_iSortedRows[m_iRows] = best_team;
		m_iIsATeam[m_iRows] = TEAM_YES;
		g_TeamInfo[best_team].already_drawn = TRUE;  // set the already_drawn to be TRUE, so this team won't get sorted again
		m_iRows++;

		// Now sort all the players on this team
		SortPlayers( 0, g_TeamInfo[best_team].name );
	}

	// Add all the players who aren't in a team yet into spectators
	SortPlayers( TEAM_SPECTATORS, NULL );
}

//-----------------------------------------------------------------------------
// Purpose: Sort a list of players
//-----------------------------------------------------------------------------
void ScorePanel::SortPlayers( int iTeam, char *team )
{
	bool bCreatedTeam = false;

	// draw the players, in order,  and restricted to team if set
	while ( 1 )
	{
		// Find the top ranking player
		int highest_frags = -99999;	int lowest_deaths = 99999;
		int best_player;
		best_player = 0;

		for ( int i = 1; i < MAX_PLAYERS; i++ )
		{
			if ( m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name && g_PlayerExtraInfo[i].frags >= highest_frags )
			{
				cl_entity_t *ent = gEngfuncs.GetEntityByIndex( i );

				if ( ent && !(team && stricmp(g_PlayerExtraInfo[i].teamname, team)) )  
				{
					extra_player_info_t *pl_info = &g_PlayerExtraInfo[i];
					if ( pl_info->frags > highest_frags || pl_info->deaths < lowest_deaths )
					{
						best_player = i;
						lowest_deaths = pl_info->deaths;
						highest_frags = pl_info->frags;
					}
				}
			}
		}

		if ( !best_player )
			break;

		// If we haven't created the Team yet, do it first
		if (!bCreatedTeam && iTeam)
		{
			m_iIsATeam[ m_iRows ] = iTeam;
			m_iRows++;

			bCreatedTeam = true;
		}

		// Put this player in the sorted list
		m_iSortedRows[ m_iRows ] = best_player;
		m_bHasBeenSorted[ best_player ] = true;
		m_iRows++;
	}

	if (team)
	{
		m_iIsATeam[m_iRows++] = TEAM_BLANK;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Recalculate the existing teams in the match
//-----------------------------------------------------------------------------
void ScorePanel::RebuildTeams()
{
	// clear out player counts from teams
	int i;
	for ( i = 1; i <= m_iNumTeams; i++ )
	{
		g_TeamInfo[i].players = 0;
	}

	// rebuild the team list
	gViewPort->GetAllPlayersInfo();
	m_iNumTeams = 0;
	for ( i = 1; i < MAX_PLAYERS; i++ )
	{
		if ( g_PlayerInfoList[i].name == NULL )
			continue;

		if ( g_PlayerExtraInfo[i].teamname[0] == 0 )
			continue; // skip over players who are not in a team

		// is this player in an existing team?
		int j;
		for ( j = 1; j <= m_iNumTeams; j++ )
		{
			if ( g_TeamInfo[j].name[0] == '\0' )
				break;

			if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) )
				break;
		}

		if ( j > m_iNumTeams )
		{ // they aren't in a listed team, so make a new one
			// search through for an empty team slot
			for ( j = 1; j <= m_iNumTeams; j++ )
			{
				if ( g_TeamInfo[j].name[0] == '\0' )
					break;
			}
			m_iNumTeams = Q_max( j, m_iNumTeams );

			strncpy( g_TeamInfo[j].name, g_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME - 1 );
			g_TeamInfo[j].players = 0;
		}

		g_TeamInfo[j].players++;
	}

	// clear out any empty teams
	for ( i = 1; i <= m_iNumTeams; i++ )
	{
		if ( g_TeamInfo[i].players < 1 )
			memset( &g_TeamInfo[i], 0, sizeof(team_info_t) );
	}

	// Update the scoreboard
	Update();
}


void ScorePanel::FillGrid()
{
	CSchemeManager *pSchemes = gViewPort->GetSchemeManager();
	SchemeHandle_t hScheme = pSchemes->getSchemeHandle("Scoreboard Text");
	SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text");
	SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text");

	Font *sfont = pSchemes->getFont(hScheme);
	Font *tfont = pSchemes->getFont(hTitleScheme);
	Font *smallfont = pSchemes->getFont(hSmallScheme);

	// update highlight position
	int x, y;
	getApp()->getCursorPos(x, y);
	cursorMoved(x, y, this);

	// remove highlight row if we're not in squelch mode
	if (!GetClientVoiceMgr()->IsInSquelchMode())
	{
		m_iHighlightRow = -1;
	}

	bool bNextRowIsGap = false;
	int row;
	for(row=0; row < NUM_ROWS; row++)
	{
		CGrid *pGridRow = &m_PlayerGrids[row];
		pGridRow->SetRowUnderline(0, false, 0, 0, 0, 0, 0);

		if(row >= m_iRows)
		{
			for(int col=0; col < NUM_COLUMNS; col++)
				m_PlayerEntries[col][row].setVisible(false);
		
			continue;
		}

		bool bRowIsGap = false;
		if (bNextRowIsGap)
		{
			bNextRowIsGap = false;
			bRowIsGap = true;
		}

		for(int col=0; col < NUM_COLUMNS; col++)
		{
			CLabelHeader *pLabel = &m_PlayerEntries[col][row];

			pLabel->setVisible(true);
			pLabel->setText2("");
			pLabel->setImage(NULL);
			pLabel->setFont(sfont);
			pLabel->setTextOffset(0, 0);
			
			int rowheight = 13;
			if (ScreenHeight > 480)
			{
				rowheight = YRES(rowheight);
			}
			else
			{
				// more tweaking, make sure icons fit at low res
				rowheight = 15;
			}
			pLabel->setSize(pLabel->getWide(), rowheight);
			pLabel->setBgColor(0, 0, 0, 255);
			
			char sz[128];
			hud_player_info_t *pl_info = NULL;
			team_info_t *team_info = NULL;

			if (m_iIsATeam[row] == TEAM_BLANK)
			{
				pLabel->setText(" ");
				continue;
			}
			else if ( m_iIsATeam[row] == TEAM_YES )
			{
				// Get the team's data
				team_info = &g_TeamInfo[ m_iSortedRows[row] ];

				// team color text for team names
				pLabel->setFgColor(	iTeamColors[team_info->teamnumber % iNumberOfTeamColors][0],
									iTeamColors[team_info->teamnumber % iNumberOfTeamColors][1],
									iTeamColors[team_info->teamnumber % iNumberOfTeamColors][2],
									0 );

				// different height for team header rows
				rowheight = 20;
				if (ScreenHeight >= 480)
				{
					rowheight = YRES(rowheight);
				}
				pLabel->setSize(pLabel->getWide(), rowheight);
				pLabel->setFont(tfont);

				pGridRow->SetRowUnderline(	0,
											true,
											YRES(3),
											iTeamColors[team_info->teamnumber % iNumberOfTeamColors][0],
											iTeamColors[team_info->teamnumber % iNumberOfTeamColors][1],
											iTeamColors[team_info->teamnumber % iNumberOfTeamColors][2],
											0 );
			}
			else if ( m_iIsATeam[row] == TEAM_SPECTATORS )
			{
				// grey text for spectators
				pLabel->setFgColor(100, 100, 100, 0);

				// different height for team header rows
				rowheight = 20;
				if (ScreenHeight >= 480)
				{
					rowheight = YRES(rowheight);
				}
				pLabel->setSize(pLabel->getWide(), rowheight);
				pLabel->setFont(tfont);

				pGridRow->SetRowUnderline(0, true, YRES(3), 100, 100, 100, 0);
			}
			else
			{
				// team color text for player names
				pLabel->setFgColor(	iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][0],
									iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][1],
									iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][2],
									0 );

				// Get the player's data
				pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ];

				// Set background color
				if ( pl_info->thisplayer ) // if it is their name, draw it a different color
				{
					// Highlight this player
					pLabel->setFgColor(Scheme::sc_white);
					pLabel->setBgColor(	iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][0],
										iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][1],
										iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][2],
										196 );
				}
				else if ( m_iSortedRows[row] == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime )
				{
					// Killer's name
					pLabel->setBgColor( 255,0,0, 255 - ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) );
				}
			}				

			// Align 
			if (col == COLUMN_NAME || col == COLUMN_CLASS)
			{
				pLabel->setContentAlignment( vgui::Label::a_west );
			}
			else if (col == COLUMN_TRACKER)
			{
				pLabel->setContentAlignment( vgui::Label::a_center );
			}
			else
			{
				pLabel->setContentAlignment( vgui::Label::a_east );
			}

			// Fill out with the correct data
			sz[0] = '\0';
			if ( m_iIsATeam[row] )
			{
				char sz2[128];

				switch (col)
				{
				case COLUMN_NAME:
					if ( m_iIsATeam[row] == TEAM_SPECTATORS )
					{
						strcpy( sz2, CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) );
					}
					else
					{
						strcpy( sz2, gViewPort->GetTeamName(team_info->teamnumber) );
					}

					strcpy(sz, sz2);

					// Append the number of players
					if ( m_iIsATeam[row] == TEAM_YES )
					{
						if (team_info->players == 1)
						{
							sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player" ) );
						}
						else
						{
							sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player_plural" ) );
						}

						pLabel->setText2(sz2);
						pLabel->setFont2(smallfont);
					}
					break;
				case COLUMN_VOICE:
					break;
				case COLUMN_CLASS:
					break;
				case COLUMN_KILLS:
					if ( m_iIsATeam[row] == TEAM_YES )
						sprintf(sz, "%d",  team_info->frags );
					break;
				case COLUMN_DEATHS:
					if ( m_iIsATeam[row] == TEAM_YES )
						sprintf(sz, "%d",  team_info->deaths );
					break;
				case COLUMN_LATENCY:
					if ( m_iIsATeam[row] == TEAM_YES )
						sprintf(sz, "%d", team_info->ping );
					break;
				default:
					break;
				}
			}
			else
			{
				bool bShowClass = false;

				switch (col)
				{
				case COLUMN_NAME:
					/*
					if (g_pTrackerUser)
					{
						int playerSlot = m_iSortedRows[row];
						int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot);
						const char *trackerName = g_pTrackerUser->GetUserName(trackerID);
						if (trackerName && *trackerName)
						{
							sprintf(sz, "   (%s)", trackerName);
							pLabel->setText2(sz);
						}
					}
					*/
					sprintf(sz, "%s  ", pl_info->name);
					break;
				case COLUMN_VOICE:
					sz[0] = 0;
					// in HLTV mode allow spectator to turn on/off commentator voice
					//if (!pl_info->thisplayer || gEngfuncs.IsSpectateOnly() )
					//{
						GetClientVoiceMgr()->UpdateSpeakerImage(pLabel, m_iSortedRows[row]);
					//}
					break;
				case COLUMN_CLASS:
					// No class for other team's members (unless allied or spectator)
					if ( gViewPort && EV_TFC_IsAllyTeam( g_iTeamNumber, g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber )  )
						bShowClass = true;
					// Don't show classes if this client hasnt picked a team yet
					if ( g_iTeamNumber == 0 )
						bShowClass = false;

					if (bShowClass)
					{
						// Only print Civilian if this team are all civilians
						bool bNoClass = false;
						if ( g_PlayerExtraInfo[ m_iSortedRows[row] ].playerclass == 0 )
						{
							if ( gViewPort->GetValidClasses( g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ) != -1 )
								bNoClass = true;
						}

						if (bNoClass)
							sz[0] = '\0';
						else
							strcpy( sz, CHudTextMessage::BufferedLocaliseTextString( sLocalisedClasses[ g_PlayerExtraInfo[ m_iSortedRows[row] ].playerclass ] ) );
					}
					else
					{
						sz[0] = '\0';
					}
					break;

				case COLUMN_TRACKER:
					/*
					if (g_pTrackerUser)
					{
						int playerSlot = m_iSortedRows[row];
						int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot);

						if (g_pTrackerUser->IsFriend(trackerID) && trackerID != g_pTrackerUser->GetTrackerID())
						{
							pLabel->setImage(m_pTrackerIcon);
							pLabel->setFgColorAsImageColor(false);
							m_pTrackerIcon->setColor(Color(255, 255, 255, 0));
						}
					}
					*/
					break;
				case COLUMN_KILLS:
					sprintf(sz, "%d",  g_PlayerExtraInfo[ m_iSortedRows[row] ].frags );
					break;
				case COLUMN_DEATHS:
					sprintf(sz, "%d",  g_PlayerExtraInfo[ m_iSortedRows[row] ].deaths );
					break;
				case COLUMN_LATENCY:
					sprintf(sz, "%d", g_PlayerInfoList[ m_iSortedRows[row] ].ping );
					break;
				default:
					break;
				}
			}

			pLabel->setText(sz);
		}
	}

	for(row=0; row < NUM_ROWS; row++)
	{
		CGrid *pGridRow = &m_PlayerGrids[row];

		pGridRow->AutoSetRowHeights();
		pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight());
		pGridRow->RepositionContents();
	}

	// hack, for the thing to resize
	m_PlayerList.getSize(x, y);
	m_PlayerList.setSize(x, y);
}


//-----------------------------------------------------------------------------
// Purpose: Setup highlights for player names in scoreboard
//-----------------------------------------------------------------------------
void ScorePanel::DeathMsg( int killer, int victim )
{
	// if we were the one killed,  or the world killed us, set the scoreboard to indicate suicide
	if ( victim == m_iPlayerNum || killer == 0 )
	{
		m_iLastKilledBy = killer ? killer : m_iPlayerNum;
		m_fLastKillTime = gHUD.m_flTime + 10;	// display who we were killed by for 10 seconds

		if ( killer == m_iPlayerNum )
			m_iLastKilledBy = m_iPlayerNum;
	}
}


void ScorePanel::Open( void )
{
	RebuildTeams();
	setVisible(true);
	m_HitTestPanel.setVisible(true);
}


void ScorePanel::mousePressed(MouseCode code, Panel* panel)
{
	if(gHUD.m_iIntermission)
		return;

	if (!GetClientVoiceMgr()->IsInSquelchMode())
	{
		GetClientVoiceMgr()->StartSquelchMode();
		m_HitTestPanel.setVisible(false);
	}
	else if (m_iHighlightRow >= 0)
	{
		// mouse has been pressed, toggle mute state
		int iPlayer = m_iSortedRows[m_iHighlightRow];
		if (iPlayer > 0)
		{
			// print text message
			hud_player_info_t *pl_info = &g_PlayerInfoList[iPlayer];

			if (pl_info && pl_info->name && pl_info->name[0])
			{
				char string[256];
				if (GetClientVoiceMgr()->IsPlayerBlocked(iPlayer))
				{
					char string1[1024];

					// remove mute
					GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, false);

					sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Unmuted" ), pl_info->name );
					sprintf( string, "%c** %s\n", HUD_PRINTTALK, string1 );

					gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, strlen(string)+1, string );
				}
				else
				{
					char string1[1024];

					// mute the player
					GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, true);

					sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Muted" ), pl_info->name );
					sprintf( string, "%c** %s %s\n", HUD_PRINTTALK, string1, CHudTextMessage::BufferedLocaliseTextString( "#No_longer_hear_that_player" ) );

					gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, strlen(string)+1, string );
				}
			}
		}
	}
}

void ScorePanel::cursorMoved(int x, int y, Panel *panel)
{
	if (GetClientVoiceMgr()->IsInSquelchMode())
	{
		// look for which cell the mouse is currently over
		for (int i = 0; i < NUM_ROWS; i++)
		{
			int row, col;
			if (m_PlayerGrids[i].getCellAtPoint(x, y, row, col))
			{
				MouseOverCell(i, col);
				return;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Handles mouse movement over a cell
// Input  : row - 
//			col - 
//-----------------------------------------------------------------------------
void ScorePanel::MouseOverCell(int row, int col)
{
	CLabelHeader *label = &m_PlayerEntries[col][row];

	// clear the previously highlighted label
	if (m_pCurrentHighlightLabel != label)
	{
		m_pCurrentHighlightLabel = NULL;
		m_iHighlightRow = -1;
	}
	if (!label)
		return;

	// don't act on teams
	if (m_iIsATeam[row] != TEAM_NO)
		return;

	// don't act on disconnected players or ourselves
	hud_player_info_t *pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ];
	if (!pl_info->name || !pl_info->name[0])
		return;

	if (pl_info->thisplayer && !gEngfuncs.IsSpectateOnly() )
		return;

	// setup the new highlight
	m_pCurrentHighlightLabel = label;
	m_iHighlightRow = row;
}

//-----------------------------------------------------------------------------
// Purpose: Label paint functions - take into account current highligh status
//-----------------------------------------------------------------------------
void CLabelHeader::paintBackground()
{
	Color oldBg;
	getBgColor(oldBg);

	if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row)
	{
		setBgColor(134, 91, 19, 0);
	}

	Panel::paintBackground();

	setBgColor(oldBg);
}
		

//-----------------------------------------------------------------------------
// Purpose: Label paint functions - take into account current highligh status
//-----------------------------------------------------------------------------
void CLabelHeader::paint()
{
	Color oldFg;
	getFgColor(oldFg);

	if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row)
	{
		setFgColor(255, 255, 255, 0);
	}

	// draw text
	int x, y, iwide, itall;
	getTextSize(iwide, itall);
	calcAlignment(iwide, itall, x, y);
	_dualImage->setPos(x, y);

	int x1, y1;
	_dualImage->GetImage(1)->getPos(x1, y1);
	_dualImage->GetImage(1)->setPos(_gap, y1);

	_dualImage->doPaint(this);

	// get size of the panel and the image
	if (_image)
	{
		Color imgColor;
		getFgColor( imgColor );
		if( _useFgColorAsImageColor )
		{
			_image->setColor( imgColor );
		}

		_image->getSize(iwide, itall);
		calcAlignment(iwide, itall, x, y);
		_image->setPos(x, y);
		_image->doPaint(this);
	}

	setFgColor(oldFg[0], oldFg[1], oldFg[2], oldFg[3]);
}


void CLabelHeader::calcAlignment(int iwide, int itall, int &x, int &y)
{
	// calculate alignment ourselves, since vgui is so broken
	int wide, tall;
	getSize(wide, tall);

	x = 0, y = 0;

	// align left/right
	switch (_contentAlignment)
	{
		// left
		case Label::a_northwest:
		case Label::a_west:
		case Label::a_southwest:
		{
			x = 0;
			break;
		}
		
		// center
		case Label::a_north:
		case Label::a_center:
		case Label::a_south:
		{
			x = (wide - iwide) / 2;
			break;
		}
		
		// right
		case Label::a_northeast:
		case Label::a_east:
		case Label::a_southeast:
		{
			x = wide - iwide;
			break;
		}
	}

	// top/down
	switch (_contentAlignment)
	{
		// top
		case Label::a_northwest:
		case Label::a_north:
		case Label::a_northeast:
		{
			y = 0;
			break;
		}
		
		// center
		case Label::a_west:
		case Label::a_center:
		case Label::a_east:
		{
			y = (tall - itall) / 2;
			break;
		}
		
		// south
		case Label::a_southwest:
		case Label::a_south:
		case Label::a_southeast:
		{
			y = tall - itall;
			break;
		}
	}

// don't clip to Y
//	if (y < 0)
//	{
//		y = 0;
//	}
	if (x < 0)
	{
		x = 0;
	}

	x += _offset[0];
	y += _offset[1];
}