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


#include "cbase.h"
#include "tf_shareddefs.h"
#include "tf_matchmaking_dashboard.h"
#include "tf_gamerules.h"
#include "ienginevgui.h"
#include "clientmode_tf.h"
#include "tf_hud_disconnect_prompt.h"
#include "tf_gc_client.h"
#include "tf_party.h"
#include "../vgui2/src/VPanel.h"

using namespace vgui;
using namespace GCSDK;

ConVar tf_mm_dashboard_spew_enabled( "tf_mm_dashboard_spew_enabled", "0", FCVAR_ARCHIVE );
#define MMDashboardSpew(...)																		\
	do {																							\
		if ( tf_mm_dashboard_spew_enabled.GetBool() )												\
		{																							\
			ConColorMsg( Color( 187, 80, 255, 255 ), "MMDashboard:" __VA_ARGS__ );					\
		} 																							\
	} while(false)																					\

extern ConVar tf_mm_next_map_vote_time;

#ifdef STAGING_ONLY 
ConVar tf_mm_dashboard_force_show( "tf_mm_dashboard_force_show", "0", 0, "Force the mm dashboard to show" );
ConVar tf_mm_popup_state_override( "tf_mm_popup_state_override", "", 0, "Force state on mm dashboard popup" );
#endif



bool BInEndOfMatch()
{
	const bool bInEndOfMatch	= TFGameRules() &&
								  TFGameRules()->State_Get() == GR_STATE_GAME_OVER &&
								  GTFGCClientSystem()->BConnectedToMatchServer( false );

	return bInEndOfMatch;
}

//-----------------------------------------------------------------------------
// Purpose: Pnael that lives on the viewport that is a popup that we parent
//			the MM dashboard panels to
//-----------------------------------------------------------------------------
class CMatchMakingHUDPopupContainer : public Panel 
{
public:
	DECLARE_CLASS_SIMPLE( CMatchMakingHUDPopupContainer, Panel );
	CMatchMakingHUDPopupContainer()
		: Panel( g_pClientMode->GetViewport(), "MMDashboardPopupContainer" )
	{
		SetProportional( true );
		SetBounds( 0, 0, g_pClientMode->GetViewport()->GetWide(), g_pClientMode->GetViewport()->GetTall() );
		MakePopup();
		SetMouseInputEnabled( true );
		SetKeyBoardInputEnabled( false ); // This can never be true
		SetVisible( false );
		ivgui()->AddTickSignal( GetVPanel(), 100 );
	}

	virtual void OnTick()
	{
		BaseClass::OnThink();

		bool bChildrenVisible = false;
		int nCount = GetChildCount();
		for( int i=0; i < nCount && !bChildrenVisible; ++i )
		{
			CExpandablePanel* pChild = assert_cast< CExpandablePanel* >( GetChild( i ) );
			bChildrenVisible = bChildrenVisible || pChild->BIsExpanded() || ( !pChild->BIsExpanded() && pChild->GetPercentAnimated() != 1.f );
		}

		SetVisible( bChildrenVisible );
	}
};

CMMDashboardParentManager::CMMDashboardParentManager()
	: m_bAttachedToGameUI( false )
{
	ListenForGameEvent( "gameui_activated" );
	ListenForGameEvent( "gameui_hidden" );

	m_pHUDPopup = new CMatchMakingHUDPopupContainer();
}

//-----------------------------------------------------------------------------
// Purpose: Update who we need to parent the MM dashboard panels to
//-----------------------------------------------------------------------------
void CMMDashboardParentManager::FireGameEvent( IGameEvent *event )
{
	if ( FStrEq( event->GetName(), "gameui_activated" ) )
	{
		m_bAttachedToGameUI = false;
	}
	else if ( FStrEq( event->GetName(), "gameui_hidden" ) )
	{
		m_bAttachedToGameUI = true;
	}

	UpdateParenting();
}

void CMMDashboardParentManager::AddPanel( CExpandablePanel* pPanel )
{
	m_vecPanels.Insert( pPanel );
	UpdateParenting();
}

void CMMDashboardParentManager::RemovePanel( CExpandablePanel* pPanel )
{
	m_vecPanels.FindAndRemove( pPanel );
	m_vecPanels.RedoSort();
	UpdateParenting();
}

void CMMDashboardParentManager::PushModalFullscreenPopup( vgui::Panel* pPanel )
{
	m_vecFullscreenPopups.AddToTail( pPanel );
	UpdateParenting();
}

void CMMDashboardParentManager::PopModalFullscreenPopup( vgui::Panel* pPanel )
{
	m_vecFullscreenPopups.FindAndRemove( pPanel );
	UpdateParenting();
}

void CMMDashboardParentManager::UpdateParenting()
{
	m_bAttachedToGameUI ? AttachToGameUI() : AttachToTopMostPopup();
}

//-----------------------------------------------------------------------------
// Purpose: Parent the MM dashboard panels to the right panels
//-----------------------------------------------------------------------------
void CMMDashboardParentManager::AttachToGameUI()
{
	if ( !m_pHUDPopup )
	{
		return;
	}

	FOR_EACH_VEC( m_vecPanels, i )
	{
		CExpandablePanel *pPanel = m_vecPanels[ i ];
		bool bKBInput = pPanel->IsKeyBoardInputEnabled();
		bool bMouseInput = pPanel->IsMouseInputEnabled();

		pPanel->SetParent( (Panel*)m_pHUDPopup );

		// Restore mouse, KV input sensitivity because MakePopup forces both to true
		pPanel->SetKeyBoardInputEnabled( bKBInput );
		pPanel->SetMouseInputEnabled( bMouseInput );
		// Don't adopt the parent's proportionalness
		pPanel->SetProportional( true );
	}
}

void CMMDashboardParentManager::AttachToTopMostPopup()
{
	// Not being used.  Hide it.
	if ( m_pHUDPopup )
	{
		m_pHUDPopup->SetVisible( false );
	}

	FOR_EACH_VEC( m_vecPanels, i )
	{	
		Panel *pPanel = m_vecPanels[ i ];
		// No longer a popup
		surface()->ReleasePanel( pPanel->GetVPanel() );
		((VPanel*)pPanel->GetVPanel())->SetPopup( false );

		if ( m_vecFullscreenPopups.Count() )
		{
			pPanel->SetParent( m_vecFullscreenPopups.Tail() );
		}
		else
		{
			pPanel->SetParent( enginevgui->GetPanel( PANEL_GAMEUIDLL ) );
		}
		pPanel->MoveToFront();
		// Don't adopt the parent's proportionalness
		pPanel->SetProportional( true );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Snag the singleton CMMDashboardParentManager
//-----------------------------------------------------------------------------
CMMDashboardParentManager* GetMMDashboardParentManager()
{
	static CMMDashboardParentManager* s_pParentManager = NULL;
	if ( !s_pParentManager )
	{
		s_pParentManager = new CMMDashboardParentManager();
	}

	return s_pParentManager;
}


void CTFMatchmakingPopup::OnEnter()
{
	MMDashboardSpew( "Entering state %s\n", GetName() );

	Update();

	SetCollapsed( false );
}

void CTFMatchmakingPopup::OnUpdate() 
{
}

void CTFMatchmakingPopup::OnExit()
{
	MMDashboardSpew( "Exiting state %s\n", GetName() );
	SetCollapsed( true );
}


void CTFMatchmakingPopup::Update()
{
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFMatchmakingPopup::CTFMatchmakingPopup( const char* pszName, const char* pszResFile )
	: CExpandablePanel( NULL, pszName )
	, m_pszResFile( pszResFile )
	, m_bActive( false )
{
	vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
	SetScheme(scheme);
	ivgui()->AddTickSignal( GetVPanel(), 100 ); 

	GetMMDashboardParentManager()->AddPanel( this );
	SetKeyBoardInputEnabled( false );

	SetProportional( true );
	
	ListenForGameEvent( "rematch_failed_to_create" );
	ListenForGameEvent( "party_updated" );
}

CTFMatchmakingPopup::~CTFMatchmakingPopup()
{
	GetMMDashboardParentManager()->RemovePanel( this );
}

void CTFMatchmakingPopup::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	SetMouseInputEnabled( true );
	LoadControlSettings( m_pszResFile );

	// This cannot ever be true or else things get weird when in-game
	SetKeyBoardInputEnabled( false );

	if ( m_bActive )
	{
		OnEnter();
	}
	else
	{
		OnExit();
	}

	GetMMDashboardParentManager()->UpdateParenting();
}

void CTFMatchmakingPopup::OnThink()
{
	BaseClass::OnThink();

	// Move us to be touching the bottom of the dashboard panel
	// These panels have no relation whatsoever, so we're doing this manually
	Panel* pDashboard = GetMMDashboard();
	int nNewYPos = Max( pDashboard->GetYPos() + pDashboard->GetTall() - YRES(10), YRES(-5) );
	SetPos( GetXPos(), nNewYPos );

	if ( m_bActive )
	{
		OnUpdate();
	}
}

void CTFMatchmakingPopup::OnTick()
{
	BaseClass::OnTick();

	bool bShouldBeActive = ShouldBeActve();
	if ( bShouldBeActive != m_bActive )
	{
		if ( bShouldBeActive )
		{
			m_bActive = true;
			OnEnter();
		}
		else
		{
			m_bActive = false;
			OnExit();
		}
	}

	SetMouseInputEnabled( ShouldBeActve() );
	SetKeyBoardInputEnabled( false ); // Never
}


void CTFMatchmakingPopup::OnCommand( const char *command )
{
	if ( FStrEq( "join_match", command ) )
	{
		JoinMatch();
	}
	else if ( FStrEq( "abandon_match", command ) )
	{
		GTFGCClientSystem()->RejoinLobby( false );
	}
	else if ( FStrEq( "leave_queue", command ) )
	{
		// Only the leader can leave the queue
		if ( GTFGCClientSystem()->BIsPartyLeader() )
		{
			switch( GTFGCClientSystem()->GetSearchMode() )
			{
			case TF_Matchmaking_LADDER:
				GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_LADDER );
				break;

			case TF_Matchmaking_CASUAL:
				GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_CASUAL );
				break;

			default:
				// Unhandled
				Assert( false );
				break;
			};
		}
	}
	else if( FStrEq( "rematch", command ) )
	{
		if ( !GTFGCClientSystem()->BIsPartyLeader() )
			return;

		engine->ClientCmd( "rematch_vote 2" );
	}
	else if ( FStrEq( "new_match", command ) )
	{
		if ( !GTFGCClientSystem()->BIsPartyLeader() )
			return;

		GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_SEARCHING );
		engine->ClientCmd( "rematch_vote 1" );
	}
	else if ( FStrEq( "leave_party", command ) )
	{
		// Leave current party and create a new one!
		GTFGCClientSystem()->SendExitMatchmaking( false );
		return;
	}
}


void CTFMatchmakingPopup::FireGameEvent( IGameEvent *pEvent )
{
	if ( FStrEq( pEvent->GetName(), "rematch_failed_to_create" ) )
	{
		// If the GC failed to create our rematch, then go ahead and requeue
		if ( !GTFGCClientSystem()->BIsPartyLeader() )
			return;

		GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_SEARCHING );
	}
	else if ( FStrEq( pEvent->GetName(), "party_updated" ) )
	{
		if ( ShouldBeActve() )
		{
			Update();
		}
	}
}


#ifdef STAGING_ONLY
CON_COMMAND( reload_mm_popup, "Reload the mm popup panel. Pass any 2nd argument to recreate the panel." )
{
	bool bRecreate = false;
	if ( args.ArgC() == 2 )
	{
		bRecreate = true;
	}

	auto& vecPopups = CreateMMPopupPanels( bRecreate );

	if ( !bRecreate )
	{
		FOR_EACH_VEC( vecPopups, i )
		{
			vecPopups[ i ]->InvalidateLayout( true, true );
		}
	}
}

CON_COMMAND( reload_mm_dashboard, "Reload the mm join panel." )
{
	GetMMDashboard()->InvalidateLayout( true, true );
}
#endif


CTFMatchmakingDashboard::CTFMatchmakingDashboard()
	: CExpandablePanel( NULL, "MMDashboard" )
{
	SetKeyBoardInputEnabled( false );
	ivgui()->AddTickSignal( GetVPanel(), 100 );

	GetMMDashboardParentManager()->AddPanel( this );

	CreateMMPopupPanels();

	vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
	SetScheme(scheme);
	SetProportional( true );
}

CTFMatchmakingDashboard::~CTFMatchmakingDashboard()
{
	GetMMDashboardParentManager()->RemovePanel( this );
}

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

	SetMouseInputEnabled( true );
	LoadControlSettings( "resource/UI/MatchMakingDashboard.res" );

	// This cannot ever be true or else things get weird when in-game
	SetKeyBoardInputEnabled( false );

	GetMMDashboardParentManager()->UpdateParenting();
}

void CTFMatchmakingDashboard::OnCommand( const char *command )
{
	if ( FStrEq( command, "disconnect" ) )
	{
		CTFParty* pParty = GTFGCClientSystem()->GetParty();
		bool bInPartyOfMany = pParty && pParty->GetNumMembers() > 1;

		const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
		if ( pMatchDesc && pMatchDesc->BShouldAutomaticallyRequeueOnMatchEnd() && !bInPartyOfMany )
		{
			// If this is an auto-requeue match type, then assume hitting the "Disconnect" button
			// means you are done playing entirely with MM.  Close out to the main menu.
			bool bNoPenalty = ( GTFGCClientSystem()->GetAssignedMatchAbandonStatus() != k_EAbandonGameStatus_AbandonWithPenalty );
			if ( bNoPenalty )
			{
				GTFGCClientSystem()->EndMatchmaking( true );			
			}
			else
			{
				// Prompt if this would be considered an abandon with a penalty
				CTFDisconnectConfirmDialog *pDialog = BuildDisconnectConfirmDialog();
				if ( pDialog )
				{
					pDialog->Show();
				}
			}
		}
		else
		{
			// Go back to the MM screens.  The party leader will request the right wizard state
			switch( GTFGCClientSystem()->GetSearchMode() )
			{
			case TF_Matchmaking_LADDER:
				if ( GTFGCClientSystem()->BIsPartyLeader() )
				{
					GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_LADDER );
				}
				engine->ClientCmd_Unrestricted( "OpenMatchmakingLobby ladder" );
				break;

			case TF_Matchmaking_CASUAL:
				if ( GTFGCClientSystem()->BIsPartyLeader() )
				{
					GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_CASUAL );
				}
				engine->ClientCmd_Unrestricted( "OpenMatchmakingLobby casual" );
				break;

			default:
				// Unhandled
				GTFGCClientSystem()->EndMatchmaking();
				Assert( false );
				break;
			};
		}

		// Disconnect from the server
		engine->DisconnectInternal();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Figure out if we should be visible and require mouse input
//-----------------------------------------------------------------------------
void CTFMatchmakingDashboard::OnTick()
{
	bool bInEndOfMatch = TFGameRules() && TFGameRules()->State_Get() == GR_STATE_GAME_OVER;

	const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules() ? TFGameRules()->GetCurrentMatchGroup() : k_nMatchGroup_Invalid );
	bool bShouldBeVisible = GTFGCClientSystem()->BConnectedToMatchServer( false )
							&& bInEndOfMatch
							&& pMatchDesc
							&& pMatchDesc->BUsesDashboard();

#ifdef STAGING_ONLY
	bShouldBeVisible |= tf_mm_dashboard_force_show.GetBool();
#endif

	if ( BIsExpanded() && !bShouldBeVisible )
	{
		SetCollapsed( true );
	}
	else if ( !BIsExpanded() && bShouldBeVisible )
	{
		SetCollapsed( false );
	}

	SetKeyBoardInputEnabled( false );
	SetMouseInputEnabled( BIsExpanded() );
}

CUtlVector< IMMPopupFactory* > IMMPopupFactory::s_vecPopupFactories;

//-----------------------------------------------------------------------------
// Purpose: Snag the singleton CTFMatchmakingPopup
//-----------------------------------------------------------------------------
CUtlVector< CTFMatchmakingPopup* >& CreateMMPopupPanels( bool bRecreate /*= false*/ )
{
	static CUtlVector< CTFMatchmakingPopup* > s_vecPopups;

	if ( bRecreate && s_vecPopups.Count() )
	{
		FOR_EACH_VEC( s_vecPopups, i )
		{
			s_vecPopups[ i ]->MarkForDeletion();
		}
		s_vecPopups.Purge();
	}


	if ( s_vecPopups.IsEmpty() )
	{
		FOR_EACH_VEC( IMMPopupFactory::s_vecPopupFactories, i )
		{
			s_vecPopups.AddToTail( IMMPopupFactory::s_vecPopupFactories[i]->Create() );
		}
	}

	return s_vecPopups;
}


//-----------------------------------------------------------------------------
// Purpose: Snag the singleton CTFMatchmakingDashboard
//-----------------------------------------------------------------------------
CTFMatchmakingDashboard* GetMMDashboard()
{
	static CTFMatchmakingDashboard* s_pDashboardPanel = NULL;
	if ( !s_pDashboardPanel )
	{
		s_pDashboardPanel = new CTFMatchmakingDashboard();
	}

	return s_pDashboardPanel;
}