//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Singleton dialog that generates and presents the entity report.
//
//===========================================================================//

#include "EntityReportPanel.h"
#include "tier1/KeyValues.h"
#include "tier1/utlbuffer.h"
#include "iregistry.h"
#include "vgui/ivgui.h"
#include "vgui_controls/listpanel.h"
#include "vgui_controls/textentry.h"
#include "vgui_controls/checkbutton.h"
#include "vgui_controls/combobox.h"
#include "vgui_controls/radiobutton.h"
#include "vgui_controls/messagebox.h"
#include "dmevmfentity.h"
#include "foundrydoc.h"
#include "foundrytool.h"

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

using namespace vgui;

	
//-----------------------------------------------------------------------------
// Sort by target name
//-----------------------------------------------------------------------------
static int __cdecl TargetNameSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
{
	const char *string1 = item1.kv->GetString("targetname");
	const char *string2 = item2.kv->GetString("targetname");
	int nRetVal = Q_stricmp( string1, string2 );
	if ( nRetVal != 0 )
		return nRetVal;

	string1 = item1.kv->GetString("classname");
	string2 = item2.kv->GetString("classname");
	return Q_stricmp( string1, string2 );
}

//-----------------------------------------------------------------------------
// Sort by class name
//-----------------------------------------------------------------------------
static int __cdecl ClassNameSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
{
	const char *string1 = item1.kv->GetString("classname");
	const char *string2 = item2.kv->GetString("classname");
	int nRetVal = Q_stricmp( string1, string2 );
	if ( nRetVal != 0 )
		return nRetVal;

	string1 = item1.kv->GetString("targetname");
	string2 = item2.kv->GetString("targetname");
	return Q_stricmp( string1, string2 );
}


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CEntityReportPanel::CEntityReportPanel( CFoundryDoc *pDoc, vgui::Panel* pParent, const char *pName )
	: BaseClass( pParent, pName ), m_pDoc( pDoc )
{
	m_bSuppressEntityListUpdate = false;
	m_iFilterByType = FILTER_SHOW_EVERYTHING;
	m_bFilterByKeyvalue = false;
	m_bFilterByClass = false;
	m_bFilterByHidden = false;
	m_bFilterByKeyvalue = false;
	m_bExact = false;
	m_bFilterTextChanged = false;

	SetPaintBackgroundEnabled( true );

	m_pEntities = new vgui::ListPanel( this, "Entities" );
	m_pEntities->AddColumnHeader( 0, "targetname", "Name", 52, ListPanel::COLUMN_RESIZEWITHWINDOW );
 	m_pEntities->AddColumnHeader( 1, "classname", "Class Name", 52, ListPanel::COLUMN_RESIZEWITHWINDOW );
	m_pEntities->SetColumnSortable( 0, true );
	m_pEntities->SetColumnSortable( 1, true );
	m_pEntities->SetEmptyListText( "No Entities" );
 //	m_pEntities->SetDragEnabled( true );
 	m_pEntities->AddActionSignalTarget( this );
	m_pEntities->SetSortFunc( 0, TargetNameSortFunc );
	m_pEntities->SetSortFunc( 1, ClassNameSortFunc );
	m_pEntities->SetSortColumn( 0 );

	// Filtering checkboxes
	m_pFilterByClass = new vgui::CheckButton( this, "ClassnameCheck", "" );
	m_pFilterByClass->AddActionSignalTarget( this );
	m_pFilterByKeyvalue = new vgui::CheckButton( this, "KeyvalueCheck", "" );
	m_pFilterByKeyvalue->AddActionSignalTarget( this );
	m_pFilterByHidden = new vgui::CheckButton( this, "HiddenCheck", "" );
	m_pFilterByHidden->AddActionSignalTarget( this );
	m_pExact = new vgui::CheckButton( this, "ExactCheck", "" );
	m_pExact->AddActionSignalTarget( this );

	// Filtering text entries
	m_pFilterKey = new vgui::TextEntry( this, "KeyTextEntry" ); 
	m_pFilterValue = new vgui::TextEntry( this, "ValueTextEntry" );

	// Classname combobox
	m_pFilterClass = new vgui::ComboBox( this, "ClassNameComboBox", 16, true );

	// Filter by type radio buttons
	m_pFilterEverything = new vgui::RadioButton( this, "EverythingRadio", "" );
	m_pFilterPointEntities = new vgui::RadioButton( this, "PointRadio", "" );
	m_pFilterBrushModels = new vgui::RadioButton( this, "BrushRadio", "" );

	LoadControlSettings( "resource/entityreportpanel.res" );

	ReadSettingsFromRegistry();

	// Used for updating filter while changing text
	ivgui()->AddTickSignal( GetVPanel(), 300 );
}


//-----------------------------------------------------------------------------
// Reads settings from registry
//-----------------------------------------------------------------------------
void CEntityReportPanel::ReadSettingsFromRegistry()
{
	m_bSuppressEntityListUpdate = true;

	const char *pKeyBase = g_pFoundryTool->GetRegistryName();
	m_pFilterByKeyvalue->SetSelected( registry->ReadInt(pKeyBase, "FilterByKeyvalue", 0) );
	m_pFilterByClass->SetSelected( registry->ReadInt(pKeyBase, "FilterByClass", 0) );
	m_pFilterByHidden->SetSelected( registry->ReadInt(pKeyBase, "FilterByHidden", 1) );
	m_pExact->SetSelected( registry->ReadInt(pKeyBase, "Exact", 0) );

	m_iFilterByType = (FilterType_t)registry->ReadInt(pKeyBase, "FilterByType", FILTER_SHOW_EVERYTHING);
	m_pFilterEverything->SetSelected( m_iFilterByType == FILTER_SHOW_EVERYTHING );
	m_pFilterPointEntities->SetSelected( m_iFilterByType == FILTER_SHOW_POINT_ENTITIES );
	m_pFilterBrushModels->SetSelected( m_iFilterByType == FILTER_SHOW_BRUSH_ENTITIES );

	// Gotta call change functions manually since SetText doesn't post an action signal
	const char *pValue = registry->ReadString( pKeyBase, "FilterClass", "" );
	m_pFilterClass->SetText( pValue );
	OnChangeFilterclass( pValue );

	pValue = registry->ReadString( pKeyBase, "FilterKey", "" );
	m_pFilterKey->SetText( pValue );
	OnChangeFilterkey( pValue );

	pValue = registry->ReadString( pKeyBase, "FilterValue", "" );
	m_pFilterValue->SetText( pValue );
	OnChangeFiltervalue( pValue );

	m_bSuppressEntityListUpdate = false;
	UpdateEntityList();
}


//-----------------------------------------------------------------------------
// Writes settings to registry
//-----------------------------------------------------------------------------
void CEntityReportPanel::SaveSettingsToRegistry()
{
	const char *pKeyBase = g_pFoundryTool->GetRegistryName();
	registry->WriteInt(pKeyBase, "FilterByKeyvalue", m_bFilterByKeyvalue);
	registry->WriteInt(pKeyBase, "FilterByClass", m_bFilterByClass);
	registry->WriteInt(pKeyBase, "FilterByHidden", m_bFilterByHidden);
	registry->WriteInt(pKeyBase, "FilterByType", m_iFilterByType);
	registry->WriteInt(pKeyBase, "Exact", m_bExact);

	registry->WriteString(pKeyBase, "FilterClass", m_szFilterClass);
	registry->WriteString(pKeyBase, "FilterKey", m_szFilterKey);
	registry->WriteString(pKeyBase, "FilterValue", m_szFilterValue);
}


//-----------------------------------------------------------------------------
// Purpose: Shows the most recent selected object in properties window
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnProperties(void)
{
	int iSel = m_pEntities->GetSelectedItem( 0 );
	KeyValues *kv = m_pEntities->GetItem( iSel );
	CDmeVMFEntity *pEntity = (CDmeVMFEntity *)kv->GetPtr( "entity" );
	g_pFoundryTool->ShowEntityInEntityProperties( pEntity );
}


//-----------------------------------------------------------------------------
// Purpose: Deletes the marked objects.
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnDeleteEntities(void)
{		
	// This is undoable
	CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Delete Entities", "Delete Entities" );

	int iSel = m_pEntities->GetSelectedItem( 0 );

	//
	// Build a list of objects to delete.
	//
	int nCount = m_pEntities->GetSelectedItemsCount();
	for (int i = 0; i < nCount; i++)
	{
		int nItemID = m_pEntities->GetSelectedItem(i);
		KeyValues *kv = m_pEntities->GetItem( nItemID );
		CDmeVMFEntity *pEntity = (CDmeVMFEntity *)kv->GetPtr( "entity" );
		if ( pEntity )
		{
			m_pDoc->DeleteEntity( pEntity );
		}
	}

	guard.Release();

	UpdateEntityList();

	// Update the list box selection.
	if (iSel >= m_pEntities->GetItemCount())
	{
		iSel = m_pEntities->GetItemCount() - 1;
	}
	m_pEntities->SetSingleSelectedItem( iSel );
}


//-----------------------------------------------------------------------------
// Called when buttons are clicked
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnCommand( const char *pCommand )
{
	if ( !Q_stricmp( pCommand, "delete" ) )
	{
		// Confirm we want to do it
		MessageBox *pConfirm = new MessageBox( "#FoundryDeleteObjects", "#FoundryDeleteObjectsMsg", g_pFoundryTool->GetRootPanel() ); 
		pConfirm->AddActionSignalTarget( this );
		pConfirm->SetOKButtonText( "Yes" );
		pConfirm->SetCommand( new KeyValues( "DeleteEntities" ) );
		pConfirm->SetCancelButtonVisible( true );
		pConfirm->SetCancelButtonText( "No" );
		pConfirm->DoModal();
		return;
	}

	if ( !Q_stricmp( pCommand, "ShowProperties" ) )
	{
		OnProperties();
		return;
	}
}

	
//-----------------------------------------------------------------------------
// Call this when our settings are dirty
//-----------------------------------------------------------------------------
void CEntityReportPanel::MarkDirty( bool bFilterDirty )
{
	float flTime = Plat_FloatTime();
	m_bRegistrySettingsChanged = true;
	m_flRegistryTime = flTime;
	if ( bFilterDirty && !m_bFilterTextChanged )
	{
		m_bFilterTextChanged = true;
		m_flFilterTime = flTime;
	}
}

	
//-----------------------------------------------------------------------------
// Methods related to filtering
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnFilterByHidden( bool bState ) 
{
	m_bFilterByHidden = bState;
	UpdateEntityList();
	MarkDirty( false );
}

void CEntityReportPanel::OnFilterByKeyvalue( bool bState ) 
{
	m_bFilterByKeyvalue = bState;
	UpdateEntityList();
	MarkDirty( false );

	m_pFilterKey->SetEnabled( bState );
	m_pFilterValue->SetEnabled( bState );
	m_pExact->SetEnabled( bState );
}

void CEntityReportPanel::OnFilterKeyValueExact( bool bState )
{
	m_bExact = bState;
	UpdateEntityList();
	MarkDirty( false );
}

void CEntityReportPanel::OnFilterByType( FilterType_t type )
{
	m_iFilterByType = type;
	UpdateEntityList();
	MarkDirty( false );
}

void CEntityReportPanel::OnFilterByClass( bool bState ) 
{
	m_bFilterByClass = bState;
	UpdateEntityList();
	MarkDirty( false );

	m_pFilterClass->SetEnabled( bState );
}

void CEntityReportPanel::OnChangeFilterkey( const char *pText ) 
{
	m_szFilterKey = pText;
	MarkDirty( true );
}

void CEntityReportPanel::OnChangeFiltervalue( const char *pText ) 
{
	m_szFilterValue = pText;
	MarkDirty( true );
}

void CEntityReportPanel::OnChangeFilterclass( const char *pText ) 
{
	m_szFilterClass = pText;
	MarkDirty( true );
}


//-----------------------------------------------------------------------------
// Deals with all check buttons
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnTextChanged( KeyValues *kv )
{
	TextEntry *pPanel = (TextEntry*)kv->GetPtr( "panel", NULL );

	int nLength = pPanel->GetTextLength();
	char *pBuf = (char*)_alloca( nLength + 1 );
	pPanel->GetText( pBuf, nLength+1 );

	if ( pPanel == m_pFilterClass )
	{
		OnChangeFilterclass( pBuf );
		return;
	}
	if ( pPanel == m_pFilterKey )
	{
		OnChangeFilterkey( pBuf );
		return;
	}
	if ( pPanel == m_pFilterValue )
	{
		OnChangeFiltervalue( pBuf );
		return;
	}
}


//-----------------------------------------------------------------------------
// Deals with all check buttons
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnButtonToggled( KeyValues *kv )
{
	Panel *pPanel = (Panel*)kv->GetPtr( "panel", NULL );
	bool bState = kv->GetInt( "state", 0 ) != 0;

	if ( pPanel == m_pFilterByClass )
	{
		OnFilterByClass( bState );
		return;
	}
	if ( pPanel == m_pFilterByKeyvalue )
	{
		OnFilterByKeyvalue( bState );
		return;
	}
	if ( pPanel == m_pFilterByHidden )
	{
		OnFilterByHidden( bState );
		return;
	}
	if ( pPanel == m_pExact )
	{
		OnFilterKeyValueExact( bState );
		return;
	}
	if ( pPanel == m_pFilterEverything )
	{
		OnFilterByType( FILTER_SHOW_EVERYTHING );
		return;
	}
	if ( pPanel == m_pFilterPointEntities )
	{
		OnFilterByType( FILTER_SHOW_POINT_ENTITIES );
		return;
	}
	if ( pPanel == m_pFilterBrushModels )
	{
		OnFilterByType( FILTER_SHOW_BRUSH_ENTITIES );
		return;
	}
}


//-----------------------------------------------------------------------------
// FIXME: Necessary because SetSelected doesn't cause a ButtonToggled message to trigger
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnCheckButtonChecked( KeyValues *kv )
{
	OnButtonToggled( kv );
}

void CEntityReportPanel::OnRadioButtonChecked( KeyValues *kv )
{
	OnButtonToggled( kv );
}


#if 0

//-----------------------------------------------------------------------------
// Purpose: Centers the 2D and 3D views on the selected entities.
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnGoto() 
{
	MarkSelectedEntities();
	m_pDoc->CenterViewsOnSelection();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::MarkSelectedEntities() 
{
	m_pDoc->SelectObject(NULL, CMapDoc::scClear);

	for(int i = 0; i < m_cEntities.GetCount(); i++)
	{
		if(!m_cEntities.GetSel(i))
			continue;
		CMapEntity *pEntity = (CMapEntity*) m_cEntities.GetItemDataPtr(i);
		m_pDoc->SelectObject(pEntity, CMapDoc::scSelect);
	}

	m_pDoc->SelectObject(NULL, CMapDoc::scUpdateDisplay);
}

#endif

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

	// check filters
	float flTime = Plat_FloatTime();
	if ( m_bFilterTextChanged )
	{
		if ( (flTime - m_flFilterTime) > 1e-3 )
		{
			m_bFilterTextChanged = false;
			m_flFilterTime = flTime;
			UpdateEntityList();
		}
	}
	if ( m_bRegistrySettingsChanged )
	{
		if ( (flTime - m_flRegistryTime) > 1e-3 )
		{
			m_bRegistrySettingsChanged = false;
			m_flRegistryTime = flTime;
			SaveSettingsToRegistry();
		}
	}
}

bool CEntityReportPanel::ShouldAddEntityToList( CDmeVMFEntity *pEntity )
{	
	// nope.
	if ( !m_bFilterByHidden && !pEntity->IsVisible() )
		return false;

	/*
	if (!pDlg->m_pDoc->selection.IsEmpty() && !pEntity->IsSelected())
		return true;
	*/

	if ( m_iFilterByType == FILTER_SHOW_POINT_ENTITIES && pEntity->IsPlaceholder() )
		return false;

	if ( m_iFilterByType == FILTER_SHOW_BRUSH_ENTITIES && !pEntity->IsPlaceholder() )
		return false;
		
	const char* pClassName = pEntity->GetClassName();

	if ( m_bFilterByClass )
	{
		if ( !m_szFilterClass.IsEmpty() )
		{
			if ( !Q_stristr( pClassName, m_szFilterClass ) )
				return false;
		}
	}

	if ( !m_bFilterByKeyvalue || m_szFilterValue.IsEmpty() )
		return true;

	CUtlBuffer buf( 256, 0, CUtlBuffer::TEXT_BUFFER );
	for ( CDmAttribute *pKey = pEntity->FirstEntityKey(); pKey; pKey = pEntity->NextEntityKey( pKey ) )
	{
		// first, check key
		if ( m_szFilterKey.IsEmpty() || !Q_stricmp( m_szFilterKey, pKey->GetName() ) )
		{
			// now, check value (as a string)
			buf.Clear();
			pKey->Serialize( buf );
			const char *pValue = (const char*)buf.Base();
			if ( (!m_bExact && Q_stristr( pValue, m_szFilterValue ) ) || !Q_stricmp( pValue, m_szFilterValue ) )
				return true;
		}
	}
	
	return false;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::UpdateEntityList(void)
{
	if ( m_bSuppressEntityListUpdate )
		return;

	m_bFilterTextChanged = false;

	m_pEntities->RemoveAll();

	const CDmrElementArray<CDmElement> entityList( m_pDoc->GetEntityList() );
	int nCount = entityList.Count();
	for ( int i = 0; i < nCount; ++i )
	{
		CDmeVMFEntity *pEntity = CastElement<CDmeVMFEntity>( entityList[i] );
		if ( ShouldAddEntityToList( pEntity ) )
		{
			const char *pClassName = pEntity->GetClassName( );
			const char *pTargetName = pEntity->GetTargetName( );
			if ( !pTargetName || !pTargetName[0] )
			{
				pTargetName = "<no name>";
			}
			if ( !pClassName || !pClassName[0] )
			{
				pClassName = "<no class>";
			}

			KeyValues *kv = new KeyValues( "node", "targetname", pTargetName );
			kv->SetString( "classname", pClassName ); 
			kv->SetPtr( "entity", pEntity );

			m_pEntities->AddItem( kv, 0, false, false );
		}
	}
	m_pEntities->SortList();
}


#if 0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::GenerateReport()
{
	POSITION p = pGD->Classes.GetHeadPosition();
	CString str;
	while(p)
	{
		GDclass *pc = pGD->Classes.GetNext(p);
		if(!pc->IsBaseClass())
		{
			str = pc->GetName();
			if(str != "worldspawn")
				m_cFilterClass.AddString(str);
		}
	}

	SetTimer(1, 500, NULL);

	OnFilterbykeyvalue();
	OnFilterbytype();
	OnFilterbyclass();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnSelChangeEntityList()
{
	MarkSelectedEntities();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnDblClkEntityList()
{
	m_pDoc->CenterViewsOnSelection();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnOK()
{
	DestroyWindow();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnClose()
{
	DestroyWindow();
}


//-----------------------------------------------------------------------------
// Purpose: Called when our window is being destroyed.
//-----------------------------------------------------------------------------
void CEntityReportPanel::OnDestroy()
{
	SaveToIni();
	s_pDlg = NULL;
	delete this;
}
#endif