/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
#include "../Game_local.h"

const static int NUM_LEADERBOARD_ITEMS = 16;
const int MAX_STAT_LISTINGS = 16;
static const int MAX_ROWS_PER_BLOCK = 50;
extern idCVar in_joylayout;
idMenuScreen_Shell_Leaderboards::~idMenuScreen_Shell_Leaderboards()
{
	if( lbCache != NULL )
	{
		delete lbCache;
		lbCache = NULL;
	}
}

// Helper functions for formatting leaderboard columns
static idStr FormatTime( int64 time )
{
	int minutes = time / ( 1000 * 60 );
	int seconds = ( time - ( minutes * 1000 * 60 ) ) / 1000;
	int mseconds = time - ( ( minutes * 1000 * 60 ) + ( seconds * 1000 ) );
	return idStr( va( "%02d:%02d.%03d", minutes, seconds, mseconds ) );
}

static idStr FormatCash( int64 cash )
{
	return idStr::FormatCash( static_cast<int32>( cash ) );
}
static int32 FormatNumber( int64 number )
{
	return static_cast<int32>( number );
}

static idSWFScriptVar FormatColumn( const columnDef_t* columnDef, int64 score )
{
	switch( columnDef->displayType )
	{
		case STATS_COLUMN_DISPLAY_TIME_MILLISECONDS:
			return idSWFScriptVar( FormatTime( score ) );
		case STATS_COLUMN_DISPLAY_CASH:
			return idSWFScriptVar( FormatCash( score ) );
		default:
			return idSWFScriptVar( FormatNumber( score ) );
	}
}

/*
========================
idMenuScreen_Shell_Leaderboards::Initialize
========================
*/
void idMenuScreen_Shell_Leaderboards::Initialize( idMenuHandler* data )
{
	common->Printf("Initializing Leaderboards\n");
	idMenuScreen::Initialize( data );
	
	if( data != NULL )
	{
		menuGUI = data->GetGUI();
	}
	
	lbCache = new idLBCache();
	lbCache->Reset();
	
	SetSpritePath( "menuLeaderboards" );
	
	options = new( TAG_SWF ) idMenuWidget_DynamicList();
	options->SetNumVisibleOptions( NUM_LEADERBOARD_ITEMS );
	options->SetSpritePath( GetSpritePath(), "info", "options" );
	options->SetWrappingAllowed( true );
	
	while( options->GetChildren().Num() < NUM_LEADERBOARD_ITEMS )
	{
		idMenuWidget_Button* const buttonWidget = new( TAG_SWF ) idMenuWidget_Button();
		buttonWidget->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_PRESS_FOCUSED, options->GetChildren().Num() );
		buttonWidget->Initialize( data );
		options->AddChild( buttonWidget );
	}
	options->Initialize( data );
	
	AddChild( options );
	
	btnBack = new( TAG_SWF ) idMenuWidget_Button();
	btnBack->Initialize( data );
	btnBack->SetLabel( "#str_swf_party_lobby" );
	btnBack->SetSpritePath( GetSpritePath(), "info", "btnBack" );
	btnBack->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_GO_BACK );
	AddChild( btnBack );
	
	btnNext = new( TAG_SWF ) idMenuWidget_Button();
	btnNext->Initialize( data );
	btnNext->SetLabel( "#str_swf_next" );
	btnNext->SetSpritePath( GetSpritePath(), "info", "btnNext" );
	btnNext->AddEventAction( WIDGET_EVENT_PRESS ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) );
	AddChild( btnNext );
	
	btnPrev = new( TAG_SWF ) idMenuWidget_Button();
	btnPrev->Initialize( data );
	btnPrev->SetLabel( "#str_swf_prev" );
	btnPrev->SetSpritePath( GetSpritePath(), "info", "btnPrevious" );
	btnPrev->AddEventAction( WIDGET_EVENT_PRESS ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) );
	AddChild( btnPrev );
	
	btnPageDwn = new( TAG_SWF ) idMenuWidget_Button();
	btnPageDwn->Initialize( data );
	btnPageDwn->SetLabel( "#str_swf_next_page" );
	btnPageDwn->SetSpritePath( GetSpritePath(), "info", "btnPageDwn" );
	idSWFParmList parms;
	parms.Append( MAX_STAT_LISTINGS - 1 );
	btnPageDwn->AddEventAction( WIDGET_EVENT_PRESS ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) );
	btnPageDwn->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) );
	AddChild( btnPageDwn );
	
	btnPageUp = new( TAG_SWF ) idMenuWidget_Button();
	btnPageUp->Initialize( data );
	btnPageUp->SetLabel( "#str_swf_prev_page" );
	btnPageUp->SetSpritePath( GetSpritePath(), "info", "btnPageUp" );
	parms.Clear();
	parms.Append( MAX_STAT_LISTINGS - 1 );
	btnPageUp->AddEventAction( WIDGET_EVENT_PRESS ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) );
	btnPageUp->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) );
	AddChild( btnPageUp );
	
	AddEventAction( WIDGET_EVENT_SCROLL_DOWN_LSTICK ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_DOWN_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_DOWN_LSTICK ) );
	AddEventAction( WIDGET_EVENT_SCROLL_UP_LSTICK ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_UP_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_UP_LSTICK ) );
	AddEventAction( WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ) );
	AddEventAction( WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ) );
	AddEventAction( WIDGET_EVENT_SCROLL_DOWN ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_DOWN_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_DOWN ) );
	AddEventAction( WIDGET_EVENT_SCROLL_UP ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_UP_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_UP ) );
	AddEventAction( WIDGET_EVENT_SCROLL_DOWN_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_RELEASE ) );
	AddEventAction( WIDGET_EVENT_SCROLL_UP_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_RELEASE ) );
	AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) );
	AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) );
	AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) );
	AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) );
	AddEventAction( WIDGET_EVENT_TAB_NEXT ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) );
	AddEventAction( WIDGET_EVENT_TAB_PREV ).Set( new( TAG_SWF ) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) );
	
	leaderboards.Clear();
	
	const idList< mpMap_t > maps = common->GetMapList();
	const char** gameModes = NULL;
	const char** gameModesDisplay = NULL;
	int numModes = game->GetMPGameModes( &gameModes, &gameModesDisplay );
	
	for( int mapIndex = 0; mapIndex < maps.Num(); ++mapIndex )
	{
		for( int modeIndex = 0; modeIndex < numModes; ++modeIndex )
		{
			// Check the supported modes on the map.
			if( maps[ mapIndex ].supportedModes & BIT( modeIndex ) )
			{
#ifdef __MONOLITH__
				int boardID = LeaderboardLocal_GetID( mapIndex, modeIndex );
				const leaderboardDefinition_t* lbDef = Sys_FindLeaderboardDef( boardID );
				if( lbDef != NULL )
				{
					doomLeaderboard_t lb = doomLeaderboard_t( lbDef, lbDef->boardName );
					leaderboards.Append( lb );
				}
#endif
			}
		}
	}
	
}

/*
========================
idMenuScreen_Shell_Leaderboards::PumpLBCache
========================
*/
void idMenuScreen_Shell_Leaderboards::PumpLBCache()
{

	if( lbCache == NULL )
	{
		return;
	}
	
	lbCache->Pump();
	
}

/*
========================
idMenuScreen_Shell_Leaderboards::ClearLeaderboard
========================
*/
void idMenuScreen_Shell_Leaderboards::ClearLeaderboard()
{

	if( lbCache == NULL )
	{
		return;
	}
	
	lbCache->Reset();
	
}

/*
========================
idMenuScreen_Shell_Leaderboards::Update
========================
*/
void idMenuScreen_Shell_Leaderboards::Update()
{

	if( menuData != NULL )
	{
		idMenuWidget_CommandBar* cmdBar = menuData->GetCmdBar();
		if( cmdBar != NULL )
		{
			cmdBar->ClearAllButtons();
			idMenuWidget_CommandBar::buttonInfo_t* buttonInfo;
			buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY2 );
			if((!common->IsNewDOOM3() && menuData->GetPlatform() != 2) || (common->IsNewDOOM3() && menuData->GetPlatform() != 5))
			{
				buttonInfo->label = "#str_00395";
			}
			buttonInfo->action.Set( WIDGET_ACTION_GO_BACK );
			
			buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY3 );
			buttonInfo->label = "#str_online_leaderboards_toggle_filter";
			buttonInfo->action.Set( WIDGET_ACTION_JOY3_ON_PRESS );
			
			if( !lbCache->IsLoadingNewLeaderboard() && !lbCache->IsRequestingRows() && options != NULL && options->GetTotalNumberOfOptions() > 0 )
			{
				buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY1 );
				if((!common->IsNewDOOM3() && menuData->GetPlatform() != 2) || (common->IsNewDOOM3() && menuData->GetPlatform() != 5))
				{
					buttonInfo->label = "#str_swf_view_profile";
				}
				buttonInfo->action.Set( WIDGET_ACTION_PRESS_FOCUSED );
			}
		}
	}
	
	idSWFScriptObject& root = GetSWFObject()->GetRootObject();
	if( BindSprite( root ) )
	{
		idSWFTextInstance* heading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtHeading" );
		if( heading != NULL )
		{
			heading->SetText( lbCache->GetFilterStrType() );
			heading->SetStrokeInfo( true, 0.75f, 1.75f );
		}
		
		idSWFSpriteInstance* gradient = GetSprite()->GetScriptObject()->GetNestedSprite( "info", "gradient" );
		if( gradient != NULL && heading != NULL )
		{
			gradient->SetXPos( heading->GetTextLength() );
		}
	}
	
	if( btnBack != NULL )
	{
		btnBack->BindSprite( root );
	}
	
	idMenuScreen::Update();
}

/*
========================
idMenuScreen_Shell_Leaderboards::ShowScreen
========================
*/
void idMenuScreen_Shell_Leaderboards::ShowScreen( const mainMenuTransition_t transitionType )
{
	idMenuScreen::ShowScreen( transitionType );
	
	if( GetSprite() != NULL )
	{
		lbHeading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtLbType" );
		if( menuData != NULL && menuData->GetGUI() != NULL )
		{
			idSWFScriptObject* const shortcutKeys = menuData->GetGUI()->GetGlobal( "shortcutKeys" ).GetObject();
			if( verify( shortcutKeys != NULL ) )
			{
			
				// TAB NEXT
				idSWFScriptObject* const btnTabNext = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnNext" );
				if( btnTabNext != NULL )
				{
					btnTabNext->Set( "onPress", new( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_NEXT, 0 ) );
					shortcutKeys->Set( "JOY6", btnTabNext );
					
					if( btnTabNext->GetSprite() != NULL && menuData != NULL )
					{
						btnTabNext->GetSprite()->StopFrame(menuData->GetPlatform() + in_joylayout.GetInteger() + 1);
						/*if (!in_joylayout.GetBool()) {
							btnTabNext->GetSprite()->StopFrame(menuData->GetPlatform() + 1);
						}
						else {
							btnTabNext->GetSprite()->StopFrame(menuData->GetPlatform() + 2);
						}*/
					}
					
				}
				
				// TAB PREV
				idSWFScriptObject* const btnTabPrev = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPrevious" );
				if( btnTabPrev != NULL )
				{
					btnTabPrev->Set( "onPress", new( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_PREV, 0 ) );
					shortcutKeys->Set( "JOY5", btnTabPrev );
					
					if( btnTabPrev->GetSprite() != NULL && menuData != NULL )
					{
						btnTabPrev->GetSprite()->StopFrame(menuData->GetPlatform() + in_joylayout.GetInteger() + 1);
						/*if (!in_joylayout.GetBool()) {
							btnTabPrev->GetSprite()->StopFrame(menuData->GetPlatform() + 1);
						}
						else {
							btnTabPrev->GetSprite()->StopFrame(menuData->GetPlatform() + 2);
						}*/
					}
				}
				
				// TAB NEXT
				idSWFScriptObject* const btnDwn = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageDwn" );
				if( btnDwn != NULL )
				{
					btnDwn->Set( "onPress", new( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEDWN, 0 ) );
					shortcutKeys->Set( "JOY_TRIGGER2", btnDwn );
					
					if( btnDwn->GetSprite() != NULL && menuData != NULL )
					{
						btnDwn->GetSprite()->StopFrame(menuData->GetPlatform() + in_joylayout.GetInteger() + 1);
						/*if (!in_joylayout.GetBool()) {
							btnDwn->GetSprite()->StopFrame(menuData->GetPlatform() + 1);
						}
						else {
							btnDwn->GetSprite()->StopFrame(menuData->GetPlatform() + 2);
						}*/
					}
					
				}
				
				// TAB PREV
				idSWFScriptObject* const btnUp = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageUp" );
				if( btnUp != NULL )
				{
					btnUp->Set( "onPress", new( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEUP, 0 ) );
					shortcutKeys->Set( "JOY_TRIGGER1", btnUp );
					
					if( btnUp->GetSprite() != NULL && menuData != NULL )
					{
						btnUp->GetSprite()->StopFrame(menuData->GetPlatform() + in_joylayout.GetInteger() + 1);
						/*if (!in_joylayout.GetBool()) {
							btnUp->GetSprite()->StopFrame(menuData->GetPlatform() + 1);
						}
						else {
							btnUp->GetSprite()->StopFrame(menuData->GetPlatform() + 2);
						}*/
					}
				}
				
			}
		}
	}
	
	SetLeaderboardIndex();
	
	if( menuData == NULL )
	{
		return;
	}
	
	int platform = menuData->GetPlatform();
	if( btnNext != NULL && btnNext->GetSprite() != NULL )
	{
		idSWFSpriteInstance* btnImg = btnNext->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
		
		if( btnImg != NULL )
		{
			if((!common->IsNewDOOM3() && platform == 2) || (common->IsNewDOOM3() && platform == 5))
			{
				btnImg->SetVisible( false );
			}
			else
			{
				btnImg->SetVisible( true );
				btnImg->StopFrame(platform + in_joylayout.GetInteger() + 1);
				/*if (!in_joylayout.GetBool()) {
					btnImg->StopFrame(platform + 1);
				}
				else {
					btnImg->StopFrame(platform + 2);
				}*/
			}
		}
	}
	
	if( btnPrev != NULL && btnPrev->GetSprite() != NULL )
	{
		idSWFSpriteInstance* btnImg = btnPrev->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
		
		if( btnImg != NULL )
		{
			if((!common->IsNewDOOM3() && platform == 2) || (common->IsNewDOOM3() && platform == 5))
			{
				btnImg->SetVisible( false );
			}
			else
			{
				btnImg->SetVisible( true );
				btnImg->StopFrame(platform + in_joylayout.GetInteger() + 1);
				/*if (!in_joylayout.GetBool()) {
					btnImg->StopFrame(platform + 1);
				}
				else {
					btnImg->StopFrame(platform + 2);
				}*/
			}
		}
	}
	
	if( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL )
	{
		idSWFSpriteInstance* btnImg = btnPageDwn->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
		
		if( btnImg != NULL )
		{
			if((!common->IsNewDOOM3() && platform == 2) || (common->IsNewDOOM3() && platform == 5))
			{
				btnImg->SetVisible( false );
			}
			else
			{
				btnImg->SetVisible( true );
				btnImg->StopFrame(platform + in_joylayout.GetInteger() + 1);
				/*if (!in_joylayout.GetBool()) {
					btnImg->StopFrame(platform + 1);
				}
				else {
					btnImg->StopFrame(platform + 2);
				}*/
			}
		}
	}
	
	if( btnPageUp != NULL && btnPageUp->GetSprite() != NULL )
	{
		idSWFSpriteInstance* btnImg = btnPageUp->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
		
		if( btnImg != NULL )
		{
			if((!common->IsNewDOOM3() && platform == 2) || (common->IsNewDOOM3() && platform == 5))
			{
				btnImg->SetVisible( false );
			}
			else
			{
				btnImg->SetVisible( true );
				btnImg->StopFrame(platform + in_joylayout.GetInteger() + 1);
				/*if (!in_joylayout.GetBool()) {
					btnImg->StopFrame(platform + 1);
				}
				else {
					btnImg->StopFrame(platform + 2);
				}*/
			}
		}
	}
	
	
}

/*
========================
idMenuScreen_Shell_Leaderboards::HideScreen
========================
*/
void idMenuScreen_Shell_Leaderboards::HideScreen( const mainMenuTransition_t transitionType )
{
	idMenuScreen::HideScreen( transitionType );
}

/*
========================
idMenuScreen_Shell_Leaderboards::HandleAction
========================
*/
bool idMenuScreen_Shell_Leaderboards::HandleAction( idWidgetAction& action, const idWidgetEvent& event, idMenuWidget* widget, bool forceHandled )
{

	if( menuData == NULL )
	{
		return true;
	}
	
	if( menuData->ActiveScreen() != SHELL_AREA_LEADERBOARDS )
	{
		return false;
	}

	this->Update();
	
	widgetAction_t actionType = action.GetType();
	const idSWFParmList& parms = action.GetParms();
	
	switch( actionType )
	{
		case WIDGET_ACTION_GO_BACK:
		{
			menuData->SetNextScreen( SHELL_AREA_PARTY_LOBBY, MENU_TRANSITION_SIMPLE );
			return true;
		}
		case WIDGET_ACTION_JOY3_ON_PRESS:
		{
			lbCache->CycleFilter();
			refreshLeaderboard = true;
			return true;
		}
		case WIDGET_ACTION_PRESS_FOCUSED:
		{
		
			if( options == NULL )
			{
				return true;
			}
			
			int index = options->GetFocusIndex();
			if( parms.Num() != 0 )
			{
				index = parms[0].ToInteger();
			}
			
			if( lbCache->GetEntryIndex() != index )
			{
				lbCache->SetEntryIndex( index );
				refreshLeaderboard = true;
				return true;
			}
			
			const idLeaderboardCallback::row_t* row = lbCache->GetLeaderboardRow( lbCache->GetRowOffset() + lbCache->GetEntryIndex() );
			if( row != NULL )
			{
				lbCache->DisplayGamerCardUI( row );
			}
			
			return true;
		}
		case WIDGET_ACTION_SCROLL_TAB:
		{
			int delta = parms[0].ToInteger();
			lbIndex += delta;
			SetLeaderboardIndex();
			return true;
		}
		case WIDGET_ACTION_SCROLL_VERTICAL_VARIABLE:
		{
			if( parms.Num() == 0 )
			{
				return true;
			}
			
			if( options == NULL )
			{
				return true;
			}
			
			int dir = parms[ 0 ].ToInteger();
			if( lbCache->Scroll( dir ) )
			{
				// TODO_SPARTY: play scroll sound
				refreshLeaderboard = true;
			}
			
			return true;
		}
		case WIDGET_ACTION_SCROLL_PAGE:
		{
			if( parms.Num() == 0 )
			{
				return true;
			}
			
			if( options == NULL )
			{
				return true;
			}
			
			int dir = parms[ 0 ].ToInteger();
			if( lbCache->ScrollOffset( dir ) )
			{
				refreshLeaderboard = true;
			}
			
			return true;
		}
	}
	
	return idMenuWidget::HandleAction( action, event, widget, forceHandled );
}

/*
========================
idMenuScreen_Shell_Leaderboards::UpdateLeaderboard
========================
*/
void idMenuScreen_Shell_Leaderboards::UpdateLeaderboard( const idLeaderboardCallback* callback )
{

	lbCache->Update( callback );
	
	if( callback->GetErrorCode() != LEADERBOARD_ERROR_NONE )
	{
		if( !session->GetSignInManager().IsMasterLocalUserOnline() )
		{
			refreshWhenMasterIsOnline = true;
		}
	}
	
	refreshLeaderboard = true;
}

/*
========================
idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex
========================
*/
void idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex()
{

	if( lbIndex >= leaderboards.Num() )
	{
		lbIndex = 0;
	}
	else if( lbIndex < 0 )
	{
		lbIndex = leaderboards.Num() - 1;
	}
	
	const leaderboardDefinition_t* leaderboardDef = leaderboards[ lbIndex ].lb;
	for( int i = 0; i < leaderboardDef->numColumns; i++ )
	{
		/*if ( leaderboardDef->columnDefs[i].displayType != STATS_COLUMN_NEVER_DISPLAY ) {
			gameLocal->GetMainMenu()->mainMenu->SetGlobal( va("columnname%d",i), leaderboardDef->columnDefs[i].locDisplayName );
		}*/
	}
	
	lbCache->SetLeaderboard( leaderboardDef, lbCache->GetFilter() );
	
	refreshLeaderboard = true;
	
}

/*
========================
idMenuScreen_Shell_Leaderboards::RefreshLeaderboard
========================
*/
void idMenuScreen_Shell_Leaderboards::RefreshLeaderboard()
{

	if( refreshWhenMasterIsOnline )
	{
		SetLeaderboardIndex();
		refreshWhenMasterIsOnline = false;
	}
	
	if( !refreshLeaderboard )
	{
		return;
	}
	
	refreshLeaderboard = false;
	bool upArrow = false;
	bool downArrow = false;
	
	int focusIndex_ = -1;
	idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > lbListings;
	
	if( !lbCache->IsLoadingNewLeaderboard() && lbCache->GetErrorCode() == LEADERBOARD_DISPLAY_ERROR_NONE )
	{
		for( int addIndex = 0; addIndex < MAX_STAT_LISTINGS; ++addIndex )
		{
		
			idList< idStr > values;
			
			int index = lbCache->GetRowOffset() + addIndex;
			
			const idLeaderboardCallback::row_t* row = lbCache->GetLeaderboardRow( index );		// If this row is not in the cache, this will kick off a request
			if( row != NULL )
			{
				values.Append( va( "%i", ( int )row->rank ) );
				values.Append( row->name );
				values.Append( FormatColumn( &lbCache->GetLeaderboard()->columnDefs[0], row->columns[0] ).ToString() );
			}
			
			if( lbCache->GetEntryIndex() == addIndex )
			{
				focusIndex_ = addIndex;
			}
			
			lbListings.Append( values );
		}
		
		if( lbCache->GetRowOffset() != 0 )
		{
			upArrow = true;
		}
		
		if( ( lbCache->GetRowOffset() + MAX_STAT_LISTINGS ) < lbCache->GetNumRowsInLeaderboard() )
		{
			downArrow = true;
		}
	}
	
	if( lbHeading != NULL )
	{
		lbHeading->SetText( leaderboards[ lbIndex ].name );
		lbHeading->SetStrokeInfo( true, 0.75f, 1.75f );
	}
	
	if( focusIndex_ >= 0 )
	{
		options->SetFocusIndex( focusIndex_ );
	}
	
	if( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL )
	{
		btnPageDwn->GetSprite()->SetVisible( downArrow );
	}
	
	if( btnPageUp != NULL && btnPageUp->GetSprite() != NULL )
	{
		btnPageUp->GetSprite()->SetVisible( upArrow );
	}
	
	options->SetListData( lbListings );
	Update();
	
	const char* leaderboardErrorStrings[] =
	{
		"",
		"#str_online_leaderboards_error_failed",			// failed
		"",													// not online - players are just taken back to multiplayer menu
		"#str_online_leaderboards_error_not_ranked",		// not ranked
	};
	
	compile_time_assert( sizeof( leaderboardErrorStrings ) / sizeof( leaderboardErrorStrings[0] ) == LEADERBOARD_DISPLAY_ERROR_MAX );
	
	bool isLoading = lbCache->IsLoadingNewLeaderboard();
	idStr error = leaderboardErrorStrings[ lbCache->GetErrorCode() ];
	
	if( isLoading )
	{
		ShowMessage( true, "#str_online_loading", true );
	}
	else
	{
		if( !error.IsEmpty() )
		{
			ShowMessage( true, error, false );
		}
		else
		{
			if( lbCache->GetNumRowsInLeaderboard() > 0 )
			{
				ShowMessage( false, "", false );
			}
			else
			{
				ShowMessage( true, "#str_online_leaderboards_no_data", false );
			}
		}
	}
}

/*
========================
idMenuScreen_Shell_Leaderboards::ShowMessage
========================
*/
void idMenuScreen_Shell_Leaderboards::ShowMessage( bool show, idStr message, bool spinner )
{

	if( !menuData || !menuData->GetGUI() )
	{
		return;
	}
	
	idSWFSpriteInstance* pacifier = menuData->GetGUI()->GetRootObject().GetNestedSprite( "menuLeaderboards", "info", "pacifier" );
	
	if( !pacifier )
	{
		return;
	}
	
	if( show )
	{
	
		if( spinner && options != NULL && options->GetSprite() != NULL )
		{
			options->GetSprite()->SetAlpha( 0.35f );
		}
		else if( options != NULL && options->GetSprite() != NULL )
		{
			options->GetSprite()->SetVisible( false );
		}
		
		pacifier->SetVisible( true );
		idSWFTextInstance* txtMsg = pacifier->GetScriptObject()->GetNestedText( "message" );
		if( txtMsg != NULL )
		{
			txtMsg->SetText( message );
			txtMsg->SetStrokeInfo( true, 0.75f, 1.75f );
		}
		
		idSWFSpriteInstance* spriteSpinner = pacifier->GetScriptObject()->GetNestedSprite( "graphic" );
		if( spriteSpinner != NULL )
		{
			spriteSpinner->StopFrame( spinner ? 1 : 2 );
		}
		
	}
	else
	{
	
		if( options != NULL && options->GetSprite() != NULL )
		{
			options->GetSprite()->SetVisible( true );
			options->GetSprite()->SetAlpha( 1.0f );
		}
		
		pacifier->SetVisible( false );
	}
	
}


//*************************************************************************************************************************
// LBCACHE
//*************************************************************************************************************************

class LBCallback : public idLeaderboardCallback
{
public:
	LBCallback() {}
	
	void Call()
	{
		game->Shell_UpdateLeaderboard( this );
	}
	
	LBCallback* Clone() const
	{
		return new LBCallback( *this );
	}
};

/*
========================
idLBCache::Pump
========================
*/
void idLBCache::Pump()
{
	if( loadingNewLeaderboard || requestingRows )
	{
		return;
	}
	
	if( pendingDef != NULL )
	{
		SetLeaderboard( pendingDef, pendingFilter );
	}
}

/*
========================
idLBCache::Reset
========================
*/
void idLBCache::Reset()
{
	for( int i = 0; i < NUM_ROW_BLOCKS; i++ )
	{
		rowBlocks[i].startIndex = 0;
		rowBlocks[i].rows.Clear();
	}
	
	def						= NULL;
	filter					= DEFAULT_LEADERBOARD_FILTER;
	pendingDef				= NULL;
	pendingFilter			= DEFAULT_LEADERBOARD_FILTER;
	rowOffset				= 0;
	requestingRows			= false;
	numRowsInLeaderboard	= 0;
	entryIndex				= 0;
	loadingNewLeaderboard	= false;
}

/*
========================
idLBCache::SetLeaderboard
========================
*/
void idLBCache::SetLeaderboard( const leaderboardDefinition_t* def_, leaderboardFilterMode_t filter_ )
{

	// If we are busy waiting on results from a previous request, queue up this request
	if( loadingNewLeaderboard || requestingRows )
	{
		pendingDef		= def_;
		pendingFilter	= filter_;
		return;
	}
	
	//idLib::Printf( "SetLeaderboard 0x%p.\n", def_ );
	
	// Reset all
	Reset();
	
	// Set leaderboard and filter
	def		= def_;
	filter	= filter_;
	
	loadingNewLeaderboard = true;		// This means we are waiting on the first set of results for this new leaderboard
	
	localIndex = -1;	// don't know where the user is in the rows yet
	
	// Kick off initial stats request (which is initially based on the filter type)
	if( filter == LEADERBOARD_FILTER_MYSCORE )
	{
		LBCallback cb;
		session->LeaderboardDownload( 0, def, 0, MAX_ROWS_PER_BLOCK, cb );
	}
	else if( filter == LEADERBOARD_FILTER_FRIENDS )
	{
		LBCallback cb;
		session->LeaderboardDownload( 0, def, -1, 100, cb );		// Request up to 100 friends
	}
	else
	{
		LBCallback cb;
		session->LeaderboardDownload( 0, def, rowOffset + 1, MAX_ROWS_PER_BLOCK, cb );
		//session->LeaderboardDownload( 0, def, rowOffset + 1, 10, cb );		// For testing
	}
}

/*
========================
idLBCache::CycleFilter
========================
*/
void idLBCache::CycleFilter()
{
	// Set the proper filter
	if( filter == LEADERBOARD_FILTER_OVERALL )
	{
		filter = LEADERBOARD_FILTER_MYSCORE;
	}
	else if( filter == LEADERBOARD_FILTER_MYSCORE )
	{
		filter = LEADERBOARD_FILTER_FRIENDS;
	}
	else
	{
		filter = LEADERBOARD_FILTER_OVERALL;
	}
	
	// Reset the leaderboard with the new filter
	SetLeaderboard( def, filter );
}

/*
========================
idLBCache::GetFilterStrType
========================
*/
idStr idLBCache::GetFilterStrType()
{
	if( filter == LEADERBOARD_FILTER_FRIENDS )
	{
		return idLocalization::GetString( "#str_swf_leaderboards_friends_heading" );
	}
	else if( filter == LEADERBOARD_FILTER_MYSCORE )
	{
		return idLocalization::GetString( "#str_swf_leaderboards_global_self_heading" );
	}
	
	return idLocalization::GetString( "#str_swf_leaderboards_global_heading" );
}

/*
========================
idLBCache::Scroll
========================
*/
bool idLBCache::Scroll( int amount )
{
	if( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE )
	{
		return false;	// don't allow scrolling on errors
	}
	
	// Remember old offsets so we know if anything moved
	int oldEntryIndex = entryIndex;
	int oldRowOffset = rowOffset;
	
	// Move cursor index by scroll amount
	entryIndex += amount;
	
	// Clamp cursor index (scrolling row offset if we can)
	if( entryIndex < 0 )
	{
		rowOffset += entryIndex;
		entryIndex = 0;
	}
	else if( entryIndex >= numRowsInLeaderboard )
	{
		entryIndex = numRowsInLeaderboard - 1;
		rowOffset = entryIndex - ( MAX_STAT_LISTINGS - 1 );
	}
	else if( entryIndex >= MAX_STAT_LISTINGS )
	{
		rowOffset += entryIndex - ( MAX_STAT_LISTINGS - 1 );
		entryIndex = MAX_STAT_LISTINGS - 1;
	}
	
	// Clamp row offset
	rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset );
	
	// Let caller know if anything actually changed
	return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset );
}

/*
========================
idLBCache::ScrollOffset
========================
*/
bool idLBCache::ScrollOffset( int amount )
{
	if( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE )
	{
		return false;	// don't allow scrolling on errors
	}
	
	// Remember old offsets so we know if anything moved
	int oldEntryIndex = entryIndex;
	int oldRowOffset = rowOffset;
	
	rowOffset += amount;
	
	// Clamp row offset
	rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset );
	
	if( rowOffset != oldRowOffset )
	{
		entryIndex -= amount;	// adjust in opposite direction so same item stays selected
		entryIndex = idMath::ClampInt( 0, rowOffset + ( MAX_STAT_LISTINGS - 1 ), entryIndex );
	}
	else
	{
		entryIndex += amount;
		entryIndex = idMath::ClampInt( 0, numRowsInLeaderboard - 1, entryIndex );
	}
	
	// Let caller know if anything actually changed
	return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset );
}

/*
========================
idLBCache::FindFreeRowBlock
========================
*/
idLBRowBlock* idLBCache::FindFreeRowBlock()
{
	int bestTime		= 0;
	int bestBlockIndex	= 0;
	
	for( int i = 0; i < NUM_ROW_BLOCKS; i++ )
	{
		if( rowBlocks[i].rows.Num() == 0 )
		{
			return &rowBlocks[i];		// Prefer completely empty blocks
		}
		
		// Search for oldest block in the mean time
		if( i == 0 || rowBlocks[i].lastTime < bestTime )
		{
			bestBlockIndex = i;
			bestTime = rowBlocks[i].lastTime;
		}
	}
	
	return &rowBlocks[bestBlockIndex];
}

/*
========================
idLBCache::CallbackErrorToDisplayError
========================
*/
leaderboardDisplayError_t idLBCache::CallbackErrorToDisplayError( leaderboardError_t _errorCode )
{
	switch( _errorCode )
	{
		case LEADERBOARD_ERROR_NONE:
			return LEADERBOARD_DISPLAY_ERROR_NONE;
		default:
			return LEADERBOARD_DISPLAY_ERROR_FAILED;
	}
}

/*
========================
idLBCache::Update
========================
*/
void idLBCache::Update( const idLeaderboardCallback* callback )
{
	requestingRows = false;
	
	//idLib::Printf( "Stats returned.\n" );
	
	errorCode = CallbackErrorToDisplayError( callback->GetErrorCode() );
	
	// check if trying to view "My Score" leaderboard when you aren't ranked yet
	if( loadingNewLeaderboard && filter == LEADERBOARD_FILTER_MYSCORE && callback->GetLocalIndex() == -1 && errorCode == LEADERBOARD_DISPLAY_ERROR_NONE )
	{
		errorCode = LEADERBOARD_DISPLAY_ERROR_NOT_RANKED;
	}
	
	if( errorCode != LEADERBOARD_DISPLAY_ERROR_NONE )
	{
		numRowsInLeaderboard = 0;
		loadingNewLeaderboard = false;
		
		switch( errorCode )
		{
			case LEADERBOARD_DISPLAY_ERROR_NOT_ONLINE:
				/*idMenuHandler_Shell * shell = gameLocal->Shell_GetHandler();
				if ( shell != NULL ) {
					shell->SetNextScreen( SHELL_AREA_ROOT, MENU_TRANSITION_SIMPLE );
				}*/
				common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, true, "", 0, true );
				break;
			default:
				break;
		}
		
		return;
	}
	
	if( callback->GetDef() != def )
	{
		// Not the leaderboard we are looking for (This should no longer be possible)
		idLib::Printf( "Wrong leaderboard.\n" );
		numRowsInLeaderboard = 0;
		loadingNewLeaderboard = false;
		return;
	}
	
	// Get total rows in this leaderboard
	numRowsInLeaderboard = callback->GetNumRowsInLeaderboard();
	
	// Store the index that the master user is in, if we haven't already found the index
	if( callback->GetLocalIndex() != -1 )
	{
		localIndex = callback->GetStartIndex() + callback->GetLocalIndex();
	}
	
	if( loadingNewLeaderboard == true )
	{
		// Default to viewing the local user (if the right filter mode is set)
		if( callback->GetLocalIndex() != -1 && ( filter == LEADERBOARD_FILTER_MYSCORE || filter == LEADERBOARD_FILTER_FRIENDS ) )
		{
			// Put their name and cursor at the top
			rowOffset = callback->GetLocalIndex() + callback->GetStartIndex();
			entryIndex = 0;
			
			// Scroll the cursor up to center their name as much as possible
			Scroll( -MAX_STAT_LISTINGS / 2 );
			
			// Set the cursor to their name
			entryIndex = ( callback->GetLocalIndex() + callback->GetStartIndex() ) - rowOffset;
		}
		loadingNewLeaderboard = false;
	}
	
	// Find a a row block to store these new rows
	idLBRowBlock* rowBlock	= FindFreeRowBlock();
	
	rowBlock->lastTime		= sys->GetMilliseconds();			// Freshen row
	rowBlock->startIndex	= callback->GetStartIndex();
	rowBlock->rows			= callback->GetRows();
}

/*
========================
idLBCache::GetLeaderboardRow
========================
*/
const idLeaderboardCallback::row_t* idLBCache::GetLeaderboardRow( int row )
{
	if( loadingNewLeaderboard )
	{
		return NULL;		// If we are refreshing (seeing this leaderboard for the first time), force NULL till we get first set of results
	}
	
	if( row >= numRowsInLeaderboard )
	{
		return NULL;
	}
	
	// Find it in the cache
	for( int i = 0; i < NUM_ROW_BLOCKS; i++ )
	{
		int startIndex = rowBlocks[i].startIndex;
		int lastIndex = startIndex + rowBlocks[i].rows.Num() - 1;
		if( row >= startIndex && row <= lastIndex )
		{
			rowBlocks[i].lastTime = sys->GetMilliseconds();		// Freshen row
			return &rowBlocks[i].rows[row - startIndex];
		}
	}
	
	// Not found, kick off a request to download it
	// (this will not allow more than one request at a time)
	if( !requestingRows )
	{
		// If we don't have this row, kick off a request to get it
		LBCallback cb;
		requestingRows = true;
		session->LeaderboardDownload( 0, def, row + 1, MAX_ROWS_PER_BLOCK, cb );
		//session->LeaderboardDownload( 0, def, row + 1, 10, cb );
		//idLib::Printf( "Stat request\n" );
	}
	
	return NULL;
}


/*
========================
idMainMenu::SetSPLeaderboardFromMenuSettings
========================
*/
void idLBCache::DisplayGamerCardUI( const idLeaderboardCallback::row_t* row )
{
}