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

#ifndef ELEMENTPROPERTIESTREE_H
#define ELEMENTPROPERTIESTREE_H

#ifdef _WIN32
#pragma once
#endif

#include "vgui_controls/Frame.h"
#include "dme_controls/AttributeWidgetFactory.h"
#include "vgui_controls/TreeView.h"
#include "vgui_controls/TreeViewListControl.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattribute.h"
#include "datamodel/dmattributevar.h"
#include "datamodel/dmehandle.h"
#include "tier1/utlntree.h"
#include "tier1/utlstring.h"
#include "tier1/utlvector.h"
#include "vgui_controls/InputDialog.h"
#include "vgui/KeyCode.h"
#include "dme_controls/inotifyui.h"


//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CDmElement;
class IDmNotify;
class CDocAllElements;
class IAttributeWidgetFactory;
class CDmeEditorTypeDictionary;
class CPropertiesTreeToolbar;

namespace vgui
{
	class TextEntry;
	class ComboBox;
	class Button;
	class PanelListPanel;
	class Menu;
}

//-----------------------------------------------------------------------------
// CElementTreeViewListControl
//-----------------------------------------------------------------------------
class CElementTreeViewListControl : public vgui::CTreeViewListControl
{
	DECLARE_CLASS_SIMPLE( CElementTreeViewListControl, CTreeViewListControl );

public:
	CElementTreeViewListControl( Panel *pParent, const char *pName );

	virtual void	ApplySchemeSettings( vgui::IScheme *pScheme );
	virtual int		AddItem( KeyValues *data, bool allowLabelEditing, int parentItemIndex, CUtlVector< vgui::Panel * >& columnPanels );
	virtual void	RemoveItem( int nItemIndex ); 
	virtual void	PerformLayout();
	virtual void	RemoveAll();
	virtual vgui::HFont	GetFont( int size );
	virtual void	SetFont( vgui::HFont font );
	virtual int		GetFontSize();
	virtual void	SetFontSize( int size );
	virtual void	PostChildPaint();
    virtual void	ExpandItem( int itemIndex, bool bExpand );
	virtual bool	IsItemExpanded( int itemIndex );
	virtual bool	IsItemSelected( int itemIndex );
	virtual KeyValues *GetItemData( int itemIndex );
	virtual int		GetTreeColumnWidth();
	virtual void	SetTreeColumnWidth( int w );
	virtual void	OnCursorMoved( int x, int y );
	virtual void	OnMousePressed( vgui::MouseCode code );
	virtual void	OnMouseReleased( vgui::MouseCode code );
	virtual void	OnMouseDoublePressed( vgui::MouseCode code );
	virtual void	OnMouseWheeled( int delta );
	virtual int		GetScrollBarSize();
	virtual void	ToggleDrawGrid();
	virtual bool	IsDrawingGrid();

	void			ResizeTreeToExpandedWidth();

private:
	struct ColumnPanels_t
	{
		ColumnPanels_t() :
			treeViewItem( -1 )
		{
		}

		ColumnPanels_t( const ColumnPanels_t& src )
		{
			treeViewItem = src.treeViewItem;
			int i, c;
			c = src.m_Columns.Count();
			for ( i = 0; i < c; ++i )
			{
				m_Columns.AddToTail( src.m_Columns[ i ] );
			}
		}

		void SetList( CUtlVector< vgui::Panel * >& list )
		{
			m_Columns.RemoveAll();
			int c = list.Count();
			for ( int i = 0; i < c; ++i )
			{
				m_Columns.AddToTail( list[ i ] );
			}
		}

		int							treeViewItem;
		CUtlVector< vgui::Panel * >	m_Columns;
	};

	// Removes an item from the tree recursively
	void RemoveItem_R( int nItemIndex );

	void	 HideAll();

	static bool PanelsLessFunc( const ColumnPanels_t& lhs, const ColumnPanels_t& rhs )
	{
		return lhs.treeViewItem < rhs.treeViewItem;
	}

	int		m_iTreeColumnWidth;
	int		m_iFontSize;  // 1 = verySmall, small, normal, large, verylarge
	bool	m_bMouseLeftIsDown;
	bool	m_bMouseIsDragging;
	bool	m_bDrawGrid;
	CUtlRBTree< ColumnPanels_t, int >	m_Panels;
};

//-----------------------------------------------------------------------------
// CElementPropertiesTreeInternal 
//-----------------------------------------------------------------------------
class CElementPropertiesTreeInternal : public vgui::EditablePanel
{
	DECLARE_CLASS_SIMPLE( CElementPropertiesTreeInternal, vgui::EditablePanel );

public:
	enum RefreshType_t
	{
		REFRESH_REBUILD = 0,	// Close the entire tree
		REFRESH_VALUES_ONLY,	// Tree topology hasn't changed; only update values
		REFRESH_TREE_VIEW,		// Tree topology changed; some attributes may be added or removed
	};

	CElementPropertiesTreeInternal( vgui::Panel *parent, IDmNotify *pNotify, 
		CDmElement *pObject, bool autoApply = true, CDmeEditorTypeDictionary *pDict = NULL );
	~CElementPropertiesTreeInternal();

	virtual void Init( );
	virtual void Refresh( RefreshType_t rebuild = REFRESH_TREE_VIEW, bool preservePrevSelectedItem = false );
	virtual void ApplyChanges();
	virtual void GenerateChildrenOfNode( int itemIndex );
	virtual void GenerateContextMenu( int itemIndex, int x, int y );
	virtual void GenerateDragDataForItem( int itemIndex, KeyValues *msg );
	virtual void OnLabelChanged( int itemIndex, char const *oldString, char const *newString );
	virtual bool IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist );
	virtual void OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist );
	virtual bool GetItemDropContextMenu( int itemIndex, vgui::Menu *menu, CUtlVector< KeyValues * >& msglist );
	virtual vgui::HCursor GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist );
	virtual void SetObject( CDmElement *object );
	virtual void OnCommand( const char *cmd );

	MESSAGE_FUNC( OnShowMemoryUsage, "OnShowMemoryUsage" );

	CDmElement *GetObject();
	bool		IsLabelBeingEdited() const;
	bool		HasItemsSelected() const;

	enum
	{
		DME_PROPERTIESTREE_MENU_BACKWARD = 0,
		DME_PROPERTIESTREE_MENU_FORWARD,
		DME_PROPERTIESTREE_MENU_SEARCHHSITORY,
	};

	virtual void	PopulateHistoryMenu( int whichMenu, vgui::Menu *menu );
	virtual int		GetHistoryMenuItemCount( int whichMenu );
	void			AddToSearchHistory( char const *str );
	void			SetTypeDictionary( CDmeEditorTypeDictionary *pDict );

	MESSAGE_FUNC_CHARPTR( OnNavSearch, "OnNavigateSearch", text );

protected:
	KeyValues *GetTreeItemData( int itemIndex );

protected:

	struct AttributeWidgets_t
	{
		vgui::Panel *m_pValueWidget;

		bool operator==( const AttributeWidgets_t &src ) const
		{
			return m_pValueWidget == src.m_pValueWidget;
		}
	};

	enum
	{
		EP_EXPANDED = (1<<0),
		EP_SELECTED = (1<<1),
	};

	struct TreeItem_t
	{
		TreeItem_t() :
			m_pElement( 0 ),
			m_pAttributeName(),
			m_pArrayElement( 0 )
		{
		}
		CDmElement *m_pElement;
		CUtlString m_pAttributeName;
		CDmElement *m_pArrayElement;	// points to the element referenced in an element array
	};

	// Used to build a list of open element for refresh
	struct TreeInfo_t
	{
		TreeInfo_t() :
			m_nFlags( 0 )
		{
		}
		
		TreeItem_t	m_Item;	// points to the element referenced in an element array

		int			m_nFlags;
		TreeItem_t	m_Preserved;
	};

	typedef CUtlNTree< TreeInfo_t, int > OpenItemTree_t;

	struct SearchResult_t
	{
		CDmeHandle< CDmElement >	handle;
		CUtlString					attributeName;

		bool operator == ( const SearchResult_t &other ) const
		{
			if ( &other == this )
				return true;

			if ( other.handle != handle )
				return false;
			if ( other.attributeName != attributeName )
				return false;

			return true;
		}
	};

	bool BuildExpansionListToFindElement_R( CUtlRBTree< CDmElement *, int >& visited, int depth, SearchResult_t& sr, CDmElement *owner, CDmElement *element, char const *attributeName, int arrayIndex, CUtlVector< int >& expandIndices );
	void FindMatchingElements_R( CUtlRBTree< CDmElement *, int >& visited, char const *searchstr, CDmElement *root, CUtlVector< SearchResult_t >& list );
	void NavigateToSearchResult();

	void SpewOpenItems( int depth, OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex );

	// Finds the tree index of a child matching the particular element + attribute
	int FindTreeItem( int nParentIndex, const TreeItem_t &info );

	// Expands all items in the open item tree if they exist
	void ExpandOpenItems( OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex, bool makeVisible );

	// Builds a list of open items
	void BuildOpenItemList( OpenItemTree_t &tree, int nParent, int nItemIndex, bool preservePrevSelectedItem );

	void FillInDataForItem( TreeItem_t &item, int nItemIndex );

	// Removes an item from the tree
	void RemoveItem( int nItemIndex );

	// Removes an item recursively
	void RemoveItem_R( int nItemIndex );

	// Adds a single entry into the tree
	void CreateTreeEntry( int parentNodeIndex, CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, AttributeWidgets_t &entry );

	// Sets up the attribute widget init info for a particular attribute
	void SetupWidgetInfo( AttributeWidgetInfo_t *pInfo, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex = -1 );

	// Creates an attribute data widget using a specifically requested widget
	vgui::Panel *CreateAttributeDataWidget( CDmElement *pElement, const char *pWidgetName, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex = -1 );

	void UpdateTree();
	void InsertAttributes( int parentNodeIndex, CDmElement *obj );
	void InsertAttributeArrayMembers( int parentNodeIndex, CDmElement *obj, CDmAttribute *array );

	// Adds a single editable attribute of the element to the tree
	void InsertSingleAttribute( int parentNodeIndex, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex = -1 );

	// Refreshes the tree view
	void RefreshTreeView( bool preservePrevSelectedItem = false );

	// Gets tree view text
	void GetTreeViewText( CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, char *pBuffer, int nMaxLen, bool& editableText );

	void	RemoveSelected( bool selectLeft );

	void			AddToHistory( CDmElement *element );

	void			ValidateHistory();
	void			SpewHistory();
	void			JumpToHistoryItem();

	void			UpdateButtonState();

	KEYBINDING_FUNC( ondelete, KEY_DELETE, 0, OnKeyDelete, "#elementpropertiestree_ondelete_help", 0 );
	KEYBINDING_FUNC( onbackspace, KEY_BACKSPACE, 0, OnKeyBackspace, "#elementpropertiestree_ondelete_help", 0 );
	KEYBINDING_FUNC( onrefresh, KEY_F5, 0, OnRefresh, "#elementpropertiestree_onrefresh_help", 0 );

	MESSAGE_FUNC( OnRename, "OnRename" );
	MESSAGE_FUNC( OnRemove, "OnRemove" );
	MESSAGE_FUNC( OnClear, "OnClear" );
	MESSAGE_FUNC( OnSortByName, "OnSortByName" );

	MESSAGE_FUNC( OnCut, "OnCut" );
	MESSAGE_FUNC( OnCopy, "OnCopy" );
	MESSAGE_FUNC( OnPaste, "OnPaste" );
	MESSAGE_FUNC( OnPasteReference, "OnPasteReference" );
	MESSAGE_FUNC( OnPasteInsert, "OnPasteInsert" );
	MESSAGE_FUNC( OnDeleteSelected, "OnDelete" );

	MESSAGE_FUNC_INT( OnElementChangedExternally, "ElementChangedExternally", valuesOnly );
	MESSAGE_FUNC_INT( OnNavBack, "OnNavigateBack", item );
	MESSAGE_FUNC_INT( OnNavForward, "OnNavigateForward", item );
	MESSAGE_FUNC_INT( OnNavigateSearchAgain, "OnNavigateSearchAgain", direction );
	MESSAGE_FUNC( OnShowSearchResults, "OnShowSearchResults" );

protected:

	MESSAGE_FUNC_PARAMS( OnInputCompleted, "InputCompleted", params );
	MESSAGE_FUNC( OnAddItem, "OnAddItem" );

	MESSAGE_FUNC_PARAMS( OnSetShared, "OnSetShared", pParams );

	MESSAGE_FUNC_PARAMS( OnChangeFile, "OnChangeFile", pParams );

	MESSAGE_FUNC_PARAMS( OnShowFileDialog, "OnShowFileDialog", pParams );
	MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", params );

	enum DropOperation_t
	{
		DO_MOVE,
		DO_LINK,
		DO_COPY,
		DO_UNKNOWN,
	};

	DropOperation_t GetDropOperation( int itemIndex, CUtlVector< KeyValues * >& msglist );

	void						DropItemsIntoArray( CDmrElementArray<> &array,
													CUtlVector< KeyValues* > &msglist,
													CUtlVector< CDmElement* > &list,
													int nArrayIndex, DropOperation_t op );

	bool						OnRemoveFromData( CUtlVector< KeyValues * >& list );
	bool						OnRemoveFromData( KeyValues *item );
	void						OnPaste_( bool reference );
	bool						ShowAddAttributeDialog( CDmElement *pElement, const char *pAttributeType );
	void						AddAttribute( const char *pAttributeName, KeyValues *pContext );
	bool						ShowSetElementAttributeDialog( CDmElement *pOwner, const char *pAttributeName, int nArrayItem, const char *pElementType );
	void						SetElementAttribute( const char *pElementName, KeyValues *pContext );
	void						OnImportElement( const char *pFullPath, KeyValues *pContext );
	void						OnExportElement( const char *pFullPath, KeyValues *pContext );

	void GetPathToItem( CUtlVector< TreeItem_t > &path, int itemIndex );
	int OpenPath( const CUtlVector< TreeItem_t > &path );

	// Refreshes the color state of the tree
	void						RefreshTreeItemState( int nItemID );

	// Refreshes the color state of the tree
	void						SetTreeItemColor( int nItemID, CDmElement *pEntryElement, bool bIsElementArrayItem, bool bEditableLabel );

	CDmeHandle< CDmeEditorTypeDictionary >	m_hTypeDictionary;
	CUtlVector< AttributeWidgets_t >		m_AttributeWidgets;
	IDmNotify					*m_pNotify;
	CDmeHandle< CDmElement >	m_hObject;
	CElementTreeViewListControl	*m_pTree;
	bool						m_bAutoApply;
	bool						m_bShowMemoryUsage;
	vgui::DHANDLE< vgui::Menu >	m_hContextMenu;

	CPropertiesTreeToolbar		*m_pToolBar; // Forward/backward navigation and search fields

	enum
	{
		DME_PROPERTIESTREE_MAXHISTORYITEMS = 32,
		DME_PROPERTIESTREE_MAXSEARCHHISTORYITEMS = 32,
	};

	// Most recent are at the head
	CUtlVector< CDmeHandle< CDmElement > >	m_hHistory;
	int										m_nCurrentHistoryPosition;
	bool									m_bSuppressHistoryUpdates;
	char									m_szSearchStr[ 128 ];

	CUtlVector< SearchResult_t >			m_SearchResults;
	int										m_nCurrentSearchResult;

	CUtlVector< CUtlString >				m_SearchHistory;

	CDmeHandle< CDmElement >				m_SearchResultsRoot;

	vgui::HCursor							m_hDragCopyCursor;
	vgui::HCursor							m_hDragLinkCursor;
	vgui::HCursor							m_hDragMoveCursor;
};


//-----------------------------------------------------------------------------
// CElementPropertiesTree 
//-----------------------------------------------------------------------------
class CElementPropertiesTree : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE( CElementPropertiesTree, vgui::Frame );
public:

	CElementPropertiesTree( vgui::Panel *parent, IDmNotify *pNotify, CDmElement *pObject, CDmeEditorTypeDictionary *pDict = NULL );

	virtual void Init( );
	virtual void Refresh( CElementPropertiesTreeInternal::RefreshType_t rebuild = CElementPropertiesTreeInternal::REFRESH_REBUILD, bool preservePrevSelectedItem = false );
	virtual void GenerateChildrenOfNode( int itemIndex );
	virtual void SetObject( CDmElement *object );
	virtual void OnCommand( const char *cmd );
	virtual void ActivateBuildMode();

	CElementPropertiesTreeInternal *GetInternal();

protected:
	CElementPropertiesTreeInternal	 *m_pProperties;
	vgui::Button		*m_pOK;
	vgui::Button		*m_pApply;
	vgui::Button		*m_pCancel;
};


//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline CElementPropertiesTreeInternal *CElementPropertiesTree::GetInternal()
{
	return m_pProperties;
}


//-----------------------------------------------------------------------------
// Wrapper panel to hook into DmePanels
//-----------------------------------------------------------------------------
class CDmeElementPanel : public CElementPropertiesTreeInternal, public IDmNotify
{
	DECLARE_CLASS_SIMPLE( CDmeElementPanel, CElementPropertiesTreeInternal );

public:
	CDmeElementPanel( vgui::Panel *pParent, const char *pPanelName );

	void SetDmeElement( CDmElement *pElement );

	// Inherited from IDmNotify
	virtual void NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags );
};

#endif // ELEMENTPROPERTIESTREE_H