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


// Valve includes
#include "tier1/KeyValues.h"

#include "vgui/MouseCode.h"
#include "vgui/IInput.h"
#include "vgui/IScheme.h"
#include "vgui/ISurface.h"

#include "vgui_controls/EditablePanel.h"
#include "vgui_controls/ScrollBar.h"
#include "vgui_controls/Label.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/Controls.h"


// Local includes
#include "dualpanellist.h"


// Last include
#include "tier0/memdbgon.h"


//=============================================================================
//
//=============================================================================
CDualPanelList::CDualPanelList( vgui::Panel *pParent, char const *pszPanelName )
: vgui::Panel( pParent, pszPanelName )
, m_pScrollBar( NULL )
{
	SetBounds( 0, 0, 100, 100 );

	if ( false )
	{
		m_pScrollBar = new vgui::ScrollBar( this, "CDualPanelListVScroll", true );
		m_pScrollBar->AddActionSignalTarget( this );
	}

	m_pPanelEmbedded = new vgui::EditablePanel( this, "PanelListEmbedded" );
	m_pPanelEmbedded->SetBounds( 0, 0, 20, 20 );
	m_pPanelEmbedded->SetPaintBackgroundEnabled( false );
	m_pPanelEmbedded->SetPaintBorderEnabled( false );

	m_iFirstColumnWidth = 100; // default width
	m_nNumColumns = 1; // 1 column by default

	if ( false && IsProportional() )
	{
		m_iDefaultHeight = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_HEIGHT );
		m_iPanelBuffer = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), PANELBUFFER );
	}
	else
	{
		m_iDefaultHeight = DEFAULT_HEIGHT;
		m_iPanelBuffer = PANELBUFFER;
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CDualPanelList::~CDualPanelList()
{
	// free data from table
	DeleteAllItems();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::SetVerticalBufferPixels( int buffer )
{
	m_iPanelBuffer = buffer;
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CDualPanelList::ComputeVPixelsNeeded()
{
	int iCurrentItem = 0;
	int iLargestH = 0;

	int nPixels = 0;
	for ( int i = 0; i < m_SortedItems.Count(); i++ )
	{
		Panel *pPanel = m_DataItems[ m_SortedItems[i] ].panel;
		if ( !pPanel || !pPanel->IsVisible() )
			continue;

		if ( pPanel->IsLayoutInvalid() )
		{
			pPanel->InvalidateLayout( true );
		}

		int iCurrentColumn = iCurrentItem % m_nNumColumns;

		int w, h;
		pPanel->GetSize( w, h );

		if ( iLargestH < h )
			iLargestH = h;

		if ( iCurrentColumn == 0 )
			nPixels += m_iPanelBuffer; // add in buffer. between rows.

		if ( iCurrentColumn >= m_nNumColumns - 1 )
		{
			nPixels += iLargestH;
			iLargestH = 0;
		}

		iCurrentItem++;
	}

	// Add in remaining largest height
	nPixels += iLargestH;

	nPixels += m_iPanelBuffer; // add in buffer below last item

	/*
	nPixels = 0;
	for ( int i = 0; i < m_SortedItems.Count(); ++i )
	{
		Panel *pPanel = m_DataItems[ m_SortedItems[i] ].panel;
		if ( !pPanel )
			continue;

		int nWide = 0;
		int nHeight = 0;
		pPanel->GetSize( nWide, nHeight );

		nPixels += nHeight;
	}
	*/

	return nPixels;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
vgui::Panel *CDualPanelList::GetCellRenderer( int row )
{
	if ( !m_SortedItems.IsValidIndex(row) )
		return NULL;

	Panel *panel = m_DataItems[ m_SortedItems[row] ].panel;
	return panel;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::AddItem( vgui::Panel *labelPanel, vgui::Panel *panel)
{
	Assert(panel);

	if ( labelPanel )
	{
		labelPanel->SetParent( m_pPanelEmbedded );
	}

	panel->SetParent( m_pPanelEmbedded );

	int itemID = m_DataItems.AddToTail();
	CDataItem &newitem = m_DataItems[itemID];
	newitem.labelPanel = labelPanel;
	newitem.panel = panel;
	m_SortedItems.AddToTail(itemID);

	InvalidateLayout();
	return itemID;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CDualPanelList::GetItemCount() const
{
	return m_DataItems.Count();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::GetItemIDFromRow( int nRow ) const
{
	if ( nRow < 0 || nRow >= GetItemCount() )
		return m_DataItems.InvalidIndex();

	return m_SortedItems[ nRow ];
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::FirstItem() const
{
	return m_DataItems.Head();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::NextItem( int nItemID ) const
{
	return m_DataItems.Next( nItemID );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::InvalidItemID() const
{
	return m_DataItems.InvalidIndex();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
vgui::Panel *CDualPanelList::GetItemLabel(int nItemID)
{
	if ( !m_DataItems.IsValidIndex( nItemID ) )
		return NULL;

	return m_DataItems[nItemID].labelPanel;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
vgui::Panel *CDualPanelList::GetItemPanel( int nItemID )
{
	if ( !m_DataItems.IsValidIndex( nItemID ) )
		return NULL;

	return m_DataItems[nItemID].panel;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDualPanelList::IsItemVisible( int nItemID ) const
{
	if ( !m_DataItems.IsValidIndex( nItemID ) )
		return true;

	return m_DataItems[nItemID].IsVisible();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::SetItemVisible( int nItemID, bool bVisible )
{
	if ( !m_DataItems.IsValidIndex( nItemID ) )
		return;

	m_DataItems[nItemID].SetVisible( bVisible );

	InvalidateLayout();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::RemoveItem( int nItemID )
{
	if ( !m_DataItems.IsValidIndex( nItemID ) )
		return;

	CDataItem &item = m_DataItems[nItemID];
	if ( item.panel )
	{
		item.panel->MarkForDeletion();
	}

	if ( item.labelPanel )
	{
		item.labelPanel->MarkForDeletion();
	}

	m_DataItems.Remove( nItemID );
	m_SortedItems.FindAndRemove( nItemID );

	InvalidateLayout();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::DeleteAllItems()
{
	FOR_EACH_LL( m_DataItems, i )
	{
		if ( m_DataItems[i].panel )
		{
			delete m_DataItems[i].panel;
		}
	}

	m_DataItems.RemoveAll();
	m_SortedItems.RemoveAll();

	InvalidateLayout();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::RemoveAll()
{
	m_DataItems.RemoveAll();
	m_SortedItems.RemoveAll();

//	m_pScrollBar->SetValue( 0 );
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::OnSizeChanged( int nWide, int nTall )
{
	BaseClass::OnSizeChanged( nWide, nTall );
	InvalidateLayout();
	Repaint();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::PerformLayout()
{
	int nWide, nTall;
	GetSize( nWide, nTall );

	int vpixels = ComputeVPixelsNeeded();

	int top = 0;
	if ( m_pScrollBar )
	{

		m_pScrollBar->SetRange( 0, vpixels );
		m_pScrollBar->SetRangeWindow( nTall );
		m_pScrollBar->SetButtonPressedScrollValue( nTall / 4 ); // standard height of labels/buttons etc.

		m_pScrollBar->SetPos( nWide - m_pScrollBar->GetWide() - 2, 0 );
		m_pScrollBar->SetSize( m_pScrollBar->GetWide(), nTall - 2 );

		top = m_pScrollBar->GetValue();
	}

	m_pPanelEmbedded->SetPos( 1, -top );
	if ( m_pScrollBar )
	{
		m_pPanelEmbedded->SetSize( nWide - m_pScrollBar->GetWide() - 2, vpixels );
	}
	else
	{
		m_pPanelEmbedded->SetSize( nWide - 2, vpixels );
	}

	SetSize( nWide, vpixels );

	/*
	bool bScrollbarVisible = true;
	// If we're supposed to automatically hide the scrollbar when unnecessary, check it now
	if ( m_bAutoHideScrollbar )
	{
		bScrollbarVisible = (m_pPanelEmbedded->GetTall() > nTall);
	}
	m_pScrollBar->SetVisible( bScrollbarVisible );
	*/

	// Now lay out the controls on the embedded panel
	int y = 0;
	int h = 0;
	int totalh = 0;

	int xpos = m_iFirstColumnWidth + m_iPanelBuffer;
	int iColumnWidth = ( nWide - xpos - 12 ) / m_nNumColumns;
	if ( m_pScrollBar )
	{
		iColumnWidth = ( nWide - xpos - m_pScrollBar->GetWide() - 12 ) / m_nNumColumns;
	}

	for ( int i = 0; i < m_SortedItems.Count(); i++ )
	{
		CDataItem &item = m_DataItems[ m_SortedItems[i] ];
		if ( !item.IsVisible() )
			continue;

		int iCurrentColumn = i % m_nNumColumns;

		// add in a little buffer between panels
		if ( iCurrentColumn == 0 )
			y += m_iPanelBuffer;

		if ( h < item.panel->GetTall() )
			h = item.panel->GetTall();

		if ( item.labelPanel )
		{
			item.labelPanel->SetBounds( 0, y, m_iFirstColumnWidth, item.panel->GetTall() );
		}

		item.panel->SetBounds( xpos + iCurrentColumn * iColumnWidth, y, iColumnWidth, item.panel->GetTall() );

		if ( iCurrentColumn >= m_nNumColumns - 1 )
		{
			y += h;
			totalh += h;

			h = 0;
		}
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	SetBorder( pScheme->GetBorder( "ButtonDepressedBorder" ) );
	SetBgColor( GetSchemeColor( "ListPanel.BgColor", GetBgColor(), pScheme ) );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::OnSliderMoved( int /* nPosition */ )
{
	InvalidateLayout();
	Repaint();
}


/*
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::MoveScrollBarToTop()
{
	m_pScrollBar->SetValue( 0 );
}
*/


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::SetFirstColumnWidth( int nWidth )
{
	m_iFirstColumnWidth = nWidth;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::GetFirstColumnWidth()
{
	return m_iFirstColumnWidth;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::SetNumColumns( int nNumColumns )
{
	m_nNumColumns = nNumColumns;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CDualPanelList::GetNumColumns()
{
	return m_nNumColumns;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::OnMouseWheeled( int nDelta )
{
	if ( !m_pScrollBar )
		return;

	int nVal = m_pScrollBar->GetValue();
	nVal -= ( nDelta * DEFAULT_HEIGHT );
	m_pScrollBar->SetValue( nVal );	
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::SetSelectedPanel( vgui::Panel *pPanel )
{
	if ( pPanel != m_hSelectedItem )
	{
		// notify the panels of the selection change
		if ( m_hSelectedItem )
		{
			PostMessage( m_hSelectedItem.Get(), new KeyValues( "PanelSelected", "state", 0 ) );
		}

		if ( pPanel )
		{
			PostMessage( pPanel, new KeyValues( "PanelSelected", "state", 1 ) );
		}

		m_hSelectedItem = pPanel;
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
vgui::Panel *CDualPanelList::GetSelectedPanel()
{
	return m_hSelectedItem;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDualPanelList::ScrollToItem( int nItemNumber )
{
	if ( !m_pScrollBar || !m_pScrollBar->IsVisible() )
		return;

	CDataItem& item = m_DataItems[ m_SortedItems[ nItemNumber ] ];
	if ( !item.panel )
		return;

	int x, y;
	item.panel->GetPos( x, y );
	int lx, ly;
	lx = x;
	ly = y;
	m_pPanelEmbedded->LocalToScreen( lx, ly );
	ScreenToLocal( lx, ly );

	int h = item.panel->GetTall();

	if ( ly >= 0 && ly + h < GetTall() )
		return;

	m_pScrollBar->SetValue( y );
	InvalidateLayout();
}