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

#include "pch_serverbrowser.h"
#include <vgui_controls/HTML.h>
#include <vgui_controls/MessageDialog.h>

using namespace vgui;

#define NUM_COMMON_TAGS			20

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
TagMenuButton::TagMenuButton(Panel *parent, const char *panelName, const char *text) : BaseClass(parent,panelName,text)
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void TagMenuButton::OnShowMenu( vgui::Menu *menu )
{
	PostActionSignal(new KeyValues("TagMenuButtonOpened"));
	BaseClass::OnShowMenu(menu);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CCustomServerInfoURLQuery : public vgui::QueryBox
{
	DECLARE_CLASS_SIMPLE( CCustomServerInfoURLQuery, vgui::QueryBox );
public:
	CCustomServerInfoURLQuery(const char *title, const char *queryText,vgui::Panel *parent) : BaseClass( title, queryText, parent )
	{
		SetOKButtonText( "#ServerBrowser_CustomServerURLButton" );
	}
};

DECLARE_BUILD_FACTORY( TagInfoLabel );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName) : BaseClass(parent,panelName, (const char *)NULL, NULL)
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : BaseClass(parent,panelName,text,pszURL)
{
}

//-----------------------------------------------------------------------------
// Purpose: If we were left clicked on, launch the URL
//-----------------------------------------------------------------------------
void TagInfoLabel::OnMousePressed(MouseCode code)
{
	if (code == MOUSE_LEFT)
	{
		if ( GetURL() )
		{
			// Pop up the dialog with the url in it
			CCustomServerInfoURLQuery *qb = new CCustomServerInfoURLQuery( "#ServerBrowser_CustomServerURLWarning", "#ServerBrowser_CustomServerURLOpen", this );
			if (qb != NULL)
			{
				qb->SetOKCommand( new KeyValues("DoOpenCustomServerInfoURL") );
				qb->AddActionSignalTarget(this);
				qb->MoveToFront();
				qb->DoModal();
			}
		}
	} 
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void TagInfoLabel::DoOpenCustomServerInfoURL( void )
{
	if ( GetURL() )
	{
		system()->ShellExecute("open", GetURL() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CCustomGames::CCustomGames(vgui::Panel *parent) : 
	BaseClass(parent, "CustomGames", eInternetServer )
{
	m_pGameList->AddColumnHeader(10, "Tags", "#ServerBrowser_Tags", 200);
	m_pGameList->SetSortFunc(10, TagsCompare);

	if ( !IsSteamGameServerBrowsingEnabled() )
	{
		m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
		m_pConnect->SetEnabled( false );
		m_pRefreshAll->SetEnabled( false );
		m_pRefreshQuick->SetEnabled( false );
		m_pAddServer->SetEnabled( false );
		m_pFilter->SetEnabled( false );
	}

	m_szTagFilter[0] = 0;

	m_pTagFilter = new TextEntry(this, "TagFilter");
	m_pTagFilter->SetEnabled( false );
	m_pTagFilter->SetMaximumCharCount( MAX_TAG_CHARACTERS );

	m_pAddTagList = new TagMenuButton( this, "AddTagList", "#ServerBrowser_AddCommonTags" );
	m_pTagListMenu = new Menu( m_pAddTagList, "TagList" );
	m_pAddTagList->SetMenu( m_pTagListMenu );
	m_pAddTagList->SetOpenDirection( Menu::UP );
	m_pAddTagList->SetEnabled( false );
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CCustomGames::~CCustomGames()
{
}		

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::UpdateDerivedLayouts( void )
{
	const char *pPathID = "PLATFORM";

	KeyValues *pConditions = NULL;
	if ( ServerBrowser().IsWorkshopEnabled() )
	{
		pConditions = new KeyValues( "conditions" );
		if ( pConditions )
		{
			KeyValues *pNewKey = new KeyValues( "if_workshop_enabled" );
			if ( pNewKey )
			{
				pConditions->AddSubKey( pNewKey );
			}
		}
	}

	if ( m_pFilter->IsSelected() )
	{
		if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage_Filters.res", "MOD" ) )
		{
			pPathID = "MOD";
		}

		LoadControlSettings( "servers/CustomGamesPage_Filters.res", pPathID, NULL, pConditions );
	}
	else
	{
		if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage.res", "MOD" ) )
		{
			pPathID = "MOD";
		}

		LoadControlSettings( "servers/CustomGamesPage.res", pPathID, NULL, pConditions );
	}

	if ( pConditions )
	{
		pConditions->deleteThis();
	}

	if ( !GameSupportsReplay() )
	{
		HideReplayFilter();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::OnLoadFilter(KeyValues *filter)
{
	BaseClass::OnLoadFilter( filter );

	Q_strncpy(m_szTagFilter, filter->GetString("gametype"), sizeof(m_szTagFilter));

	if ( m_pTagFilter )
	{
		m_pTagFilter->SetText(m_szTagFilter);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CCustomGames::CheckTagFilter( gameserveritem_t &server )
{
	bool bRetVal = true;

	// Custom games substring matches tags with the server's tags
	int count = Q_strlen( m_szTagFilter );
	if ( count )
	{
		CUtlVector<char*> TagList;
		V_SplitString( m_szTagFilter, ",", TagList );
		for ( int i = 0; i < TagList.Count(); i++ )
		{
			if ( ( Q_strnistr( server.m_szGameTags, TagList[i], MAX_TAG_CHARACTERS ) != 0 ) == TagsExclude() )
			{
				bRetVal = false;
				break;
			}
		}

		TagList.PurgeAndDeleteElements();
	}

	return bRetVal;
}

//-----------------------------------------------------------------------------
// Purpose: Checks the workshop filtering setting, taking into account workshop filtering might be disabled
//-----------------------------------------------------------------------------
bool CCustomGames::CheckWorkshopFilter( gameserveritem_t &server )
{
	eWorkshopMode workshopMode = WorkshopMode();
	const char szWorkshopPrefix[] = "workshop/";
	if ( workshopMode == eWorkshop_WorkshopOnly )
	{
		return V_strncasecmp( server.m_szMap, szWorkshopPrefix, sizeof( szWorkshopPrefix ) - 1 ) == 0;
	}
	else if ( workshopMode == eWorkshop_SubscribedOnly )
	{
		return ServerBrowser().IsWorkshopSubscribedMap( server.m_szMap );
	}

	Assert( workshopMode == eWorkshop_None );
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: gets filter settings from controls
//-----------------------------------------------------------------------------
void CCustomGames::OnSaveFilter(KeyValues *filter)
{
	BaseClass::OnSaveFilter( filter );

	if ( m_pTagFilter )
	{
		// tags
		m_pTagFilter->GetText(m_szTagFilter, sizeof(m_szTagFilter) - 1);
	}

	if ( m_szTagFilter[0] )
	{
		Q_strlower(m_szTagFilter);
	}

	if ( TagsExclude() )
	{
		m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", "" ) );
	}
	else
	{
		m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", m_szTagFilter ) );
	}

	filter->SetString("gametype", m_szTagFilter);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::SetRefreshing(bool state)
{
	if ( state )
	{
		m_pAddTagList->SetEnabled( false );
	}

	BaseClass::SetRefreshing( state );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::ServerResponded( int iServer, gameserveritem_t *pServerItem )
{
	CBaseGamesPage::ServerResponded( iServer, pServerItem );

	// If we've found a server with some tags, enable the add tag button
	if ( pServerItem->m_szGameTags[0] )
	{
		m_pAddTagList->SetEnabled( true );
	}
}

struct tagentry_t
{
	const char *pszTag;
	int iCount;
};
int __cdecl SortTagsInUse( const tagentry_t *pTag1, const tagentry_t *pTag2 )
{
	return (pTag1->iCount < pTag2->iCount);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::RecalculateCommonTags( void )
{
	// Regenerate our tag list
	m_pTagListMenu->DeleteAllItems();

	// Loop through our servers, and build a list of all the tags
	CUtlVector<tagentry_t> aTagsInUse;

	int iCount = m_pGameList->GetItemCount();
	for ( int i = 0; i < iCount; i++ )
	{
		int serverID = m_pGameList->GetItemUserData( i );
		gameserveritem_t *pServer = GetServer( serverID ); 
		if ( pServer && pServer->m_szGameTags && pServer->m_szGameTags[0] )
		{
			CUtlVector<char*> TagList;
			V_SplitString( pServer->m_szGameTags, ",", TagList );

			for ( int iTag = 0; iTag < TagList.Count(); iTag++ )
			{
				// First make sure it's not already in our list
				bool bFound = false;
				for ( int iCheck = 0; iCheck < aTagsInUse.Count(); iCheck++ )
				{
					if ( !Q_strnicmp(TagList[iTag], aTagsInUse[iCheck].pszTag, MAX_TAG_CHARACTERS ) )
					{
						aTagsInUse[iCheck].iCount++;
						bFound = true;
					}
				}

				if ( !bFound )
				{
					int iIdx = aTagsInUse.AddToTail();
					aTagsInUse[iIdx].pszTag = TagList[iTag];
					aTagsInUse[iIdx].iCount = 0;
				}
			}
		}
	}

	aTagsInUse.Sort( SortTagsInUse );

	int iTagsToAdd = min( aTagsInUse.Count(), NUM_COMMON_TAGS );
	for ( int i = 0; i < iTagsToAdd; i++ )
	{
		const char *pszTag = aTagsInUse[i].pszTag;
		m_pTagListMenu->AddMenuItem( pszTag, new KeyValues("AddTag", "tag", pszTag), this, new KeyValues( "data", "tag", pszTag ) );
	}

	m_pTagListMenu->SetFixedWidth( m_pAddTagList->GetWide() );
	m_pTagListMenu->InvalidateLayout( true, false );
	m_pTagListMenu->PositionRelativeToPanel( m_pAddTagList, Menu::UP );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::OnTagMenuButtonOpened( void )
{
	RecalculateCommonTags();
}

//-----------------------------------------------------------------------------
// Purpose: Sets the text from the message
//-----------------------------------------------------------------------------
void CCustomGames::OnAddTag(KeyValues *params)
{
	KeyValues *pkvText = params->FindKey("tag", false);
	if (!pkvText)
		return;

	AddTagToFilterList( pkvText->GetString() );
}


int SortServerTags( char* const *p1, char* const *p2 )
{
	return ( Q_strcmp( *p1, *p2 ) > 0 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCustomGames::AddTagToFilterList( const char *pszTag )
{
	char txt[ 128 ];
	m_pTagFilter->GetText( txt, sizeof( txt ) );

	CUtlVector<char*> TagList;
	V_SplitString( txt, ",", TagList );

	if ( txt[0] )
	{
		for ( int i = 0; i < TagList.Count(); i++ )
		{
			// Already in the tag list?
			if ( !Q_stricmp( TagList[i], pszTag ) )
			{
				TagList.PurgeAndDeleteElements();
				return;
			}
		}
	}

	char *pszNewTag = new char[64];
	Q_strncpy( pszNewTag, pszTag, 64 );
	TagList.AddToHead( pszNewTag );

	TagList.Sort( SortServerTags );

	// Append it
	char tmptags[MAX_TAG_CHARACTERS];
	tmptags[0] = '\0';

	for ( int i = 0; i < TagList.Count(); i++ )
	{
		if ( i > 0 )
		{
			Q_strncat( tmptags, ",", MAX_TAG_CHARACTERS );
		}

		Q_strncat( tmptags, TagList[i], MAX_TAG_CHARACTERS );
	}

	m_pTagFilter->SetText( tmptags );
	TagList.PurgeAndDeleteElements();

	// Update & apply filters now that the tag list has changed
	UpdateFilterSettings();
	ApplyGameFilters();
}