//
// Asura_Tree_Dialog.h
//
// Created by Richard Rice - 11/09/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//


#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_TREE_DIALOG_H__
#define __ASURA_TREE_DIALOG_H__

#include "Core/Asura_String.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Gadget.h"


class Asura_TreeDialog_Tree;
class Asura_TreeDialog_Item;
class Asura_TreeView_Gadget;

//enumeration of icons within the tree icons texture
enum ASURA_TREEDIALOG_ICON
{
	ASURA_TREEDIALOG_ICON_PLUS,
	ASURA_TREEDIALOG_ICON_MINUS,
	ASURA_TREEDIALOG_ICON_CLOSEDFOLDER,
	ASURA_TREEDIALOG_ICON_OPENEDFOLDER,
	ASURA_TREEDIALOG_ICON_WRITEABLEFILE,
	ASURA_TREEDIALOG_ICON_READONLYFILE,
	ASURA_TREEDIALOG_ICON_VISIBLE,
	ASURA_TREEDIALOG_ICON_HIDDEN,
	ASURA_TREEDIALOG_ICON_ATTACHMENT,
	ASURA_TREEDIALOG_ICON_ATTACHMENT_HIGHLIGHT,
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	ASURA_TREEDIALOG_ICON_PADLOCK,
	#endif
};

//////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------**
** Asura_Tree_Dialog **
**-------------------*/

/*
Asura_Tree_Dialog creates a control for displaying a tree view , similar to that used by windows explorer and the like.
It requires that an Asura_TreeDialog_Tree be created first that contains all the details of the tree's structure.
Note that since the tree contains details of which branches have been expanded and of the current selection ,it isn't
practical to have the same tree instance open in two controls at the same time.
*/

class Asura_Tree_Dialog : public Asura_Dialog
{
public :
	//Creates a tree control
	//pxTree is the tree to be shown
	//fWidth and fHeight are the controls size in print coordinates
	//pbSelectionChanged is an optional flag that will be set when the selection is changed by the user
	Asura_Tree_Dialog(const Asura_Vector_2& xOffset,Asura_TreeDialog_Tree* pxTree,float fWidth,float fHeight,bool* pbSelectionChanged = 0);

	//Ensures that the current selection is in the visible part of the tree view
	//Mainly for internal use , but may be useful if changing the selection externally
	void MakeSelectionVisible();

    // The double clicked function is called when a tree node is double clicked, before it is automatically expanded.
    void SetDoubleClickedFunction(void(*pfnOnClicked)(void));
    void SetDoubleClickedFunction(void(*pfnOnClicked)(int), int iUserData);
    void SetDoubleClickedFunction(const Asura_Callback_Base<void>& xCallback);

	// Sets callback to be called in response to a drag/drop operation.
	// The Asura_TreeDialog_Item* param is the target of the drag
	void SetDragDropCallback(const Asura_Callback_Base_1Arg<void,Asura_TreeDialog_Item*>& xCallback);

	// Sets callback used for determining the icon to render in place of the open/closed folder icon.
	// The callback should return a negative value if none should be shown
	void SetIconCallback(const Asura_Callback_Base_1Arg<int,Asura_TreeDialog_Item*>& xCallback);

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	// Sets callback used for determining the additional icon to render
	// The callback should return a negative value if none should be shown
	void SetAdditionalIconCallback(const Asura_Callback_Base_2Arg<int,Asura_TreeDialog_Item*,u_int>& xCallback);
	#endif

	//Sets callback to use when the user attempts to rename a tree item.
	//Arguments passed are the item being renamed and the proposed new name
	void SetRenameCallback(const Asura_Callback_Base_2Arg<void,Asura_TreeDialog_Item* /*pxRenamedItem*/,const char* /*szNewName*/>& xCallback);

	//Sets callback to use when the user attempts to click on the folder(or replacement) icon
	//Arguments passed are the item being clicked on and whether it is the continuation of a drag operaion
	void SetIconClickCallback(const Asura_Callback_Base_2Arg<void,Asura_TreeDialog_Item* /*pxClickedItem*/,bool /*bDrag*/>& xCallback);

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	//Sets callback to use when the user attempts to click on the folder(or replacement) icon
	//Arguments passed are the item being clicked on and whether it is the continuation of a drag operaion
	void SetAdditionalIconClickCallback(const Asura_Callback_Base_2Arg<void,Asura_TreeDialog_Item* /*pxClickedItem*/,bool /*bDrag*/>& xCallback);
	#endif

	void SetSelectionChangedCallback( const Asura_Callback_Base_1Arg< void, Asura_TreeDialog_Item* >& xCallback );

	//Functions mainly used by the tree view gadget :

	//Get the iIndex'th visible item
	Asura_TreeDialog_Item* GetVisibleItem(int iIndex) const;

	float GetVerticalScrollPos() const {return m_fVScrollPos;}
	float GetHorizontalScrollPos() const {return m_fHScrollPos;}

	float GetMaxVerticalScroll() const {return m_pxVerticalBar->GetMax();}
	float GetViewHeight() const {return m_fViewHeight;}

	//scroll up/down by the specified amount
	void VerticalScroll(float fScrollAmount);

	Asura_TreeDialog_Tree* GetTree() { return m_pxTree; }
	const Asura_TreeDialog_Tree* GetTree() const { return m_pxTree; }

	//if true, then lift help text from the currently hovered item
	bool UsePerItemHelp() const { return m_bUsePerItemHelp; }
	void SetUsePerItemHelp(bool bUsePerItemHelp) { m_bUsePerItemHelp = bUsePerItemHelp; }

	//Allows control to prepare for the overlay being resized
	virtual void StartResize(u_int uResizeFlags);
	//Handles user resizing the overlay. xDelta is the cumulative delta from the start of the operation
	virtual void UpdateResize(u_int uResizeFlags, const Asura_Vector_2& xDelta);
	//Gets the direction(s) in which this dialog can be resized
	virtual u_int GetAllowedResizeFlags() const;

	//used by the gadget to handle renaming items on the tree
	void StartRename(Asura_TreeDialog_Item* pxItem);
	void UpdateRename();
	void CancelRename();
	Asura_TreeDialog_Item* GetItemBeingRenamed() const {return m_pxRenameItem;} 


protected :
	virtual void Render( const Asura_Vector_2& xOrigin );

	virtual void PerFrameUpdate();

private :
	void CreateGadgets(float fWidth, float fHeight);
	void UpdateScrollbars();

	void CallRenameCallback();

	Asura_TreeDialog_Tree* m_pxTree;
    Asura_TreeView_Gadget* m_pxTreeViewGadget;

	Asura_Scrollbar_Gadget* m_pxHorizontalBar;
	Asura_Scrollbar_Gadget* m_pxVerticalBar;

	float m_fHScrollPos;
	float m_fVScrollPos;

	//width/height of main tree view gadget
	float m_fViewWidth;
	float m_fViewHeight;

	bool* m_pbSelectionChanged;

	//if true, then lift help text from the currently hovered item
	bool m_bUsePerItemHelp;

	float m_fPreResizeWidth;
	float m_fPreResizeHeight;

	//for dealing with the string gadget for renaming
	Asura_String_Gadget* m_pxRenameStringGadget;
	Asura_String m_xRenameString;
	Asura_TreeDialog_Item* m_pxRenameItem;
	bool m_bRenameAccepted;

	Asura_Callback_Wrapper<void> m_xDoubleClickCallback;
	Asura_Callback_Wrapper_1Arg<void,Asura_TreeDialog_Item*> m_xDragDropCallback;
	Asura_Callback_Wrapper_1Arg<int,Asura_TreeDialog_Item*> m_xIconCallback;
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	Asura_Callback_Wrapper_2Arg<int,Asura_TreeDialog_Item*,u_int> m_xAdditionalIconCallback;
	#endif
	Asura_Callback_Wrapper_2Arg<void,Asura_TreeDialog_Item*,const char*> m_xRenameCallback;
	Asura_Callback_Wrapper_2Arg<void,Asura_TreeDialog_Item*,bool> m_xIconClickCallback;
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	Asura_Callback_Wrapper_2Arg<void,Asura_TreeDialog_Item*,bool> m_xAdditionalIconClickCallback;
	#endif
	Asura_Callback_Wrapper_1Arg<void,Asura_TreeDialog_Item*> m_xSelectionChangedCallback;
};

/////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** Asura_TreeView_Gadget **
**-----------------------*/

//enumeration of parts of the line that the cursor can be over
enum Asura_Tree_LineElement
{
	ASURA_TREE_LINEELEMENT_EXPAND,
	ASURA_TREE_LINEELEMENT_ICON,
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	ASURA_TREE_LINEELEMENT_ADDITIONAL_ICON,
	#endif
	ASURA_TREE_LINEELEMENT_NAME,
};

//gadget for the main area of the tree view
class Asura_TreeView_Gadget : public Asura_Gadget
{
public :
	Asura_TreeView_Gadget(const Asura_Vector_2& xOffset,Asura_Tree_Dialog* pxTreeDialog,Asura_TreeDialog_Tree* pxTree,float fWidth,float fHeight,bool* pbSelectionChanged);


	void SetDoubleClickCallback(const Asura_Callback_Base<void>* pxCallback){m_xDoubleClickCallback.SetCallback(pxCallback);}
  	void SetDragDropCallback(const Asura_Callback_Base_1Arg<void,Asura_TreeDialog_Item*>* pxCallback) {m_xDragDropCallback.SetCallback(pxCallback);}
	void SetIconCallback(const Asura_Callback_Base_1Arg<int,Asura_TreeDialog_Item*>* pxCallback) {m_xIconCallback.SetCallback(pxCallback);}
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	void SetAdditionalIconCallback(const Asura_Callback_Base_2Arg<int,Asura_TreeDialog_Item*, u_int>* pxCallback) {m_xAdditionalIconCallback.SetCallback(pxCallback);}
	void SetAdditionalIconClickCallback(const Asura_Callback_Base_2Arg<void,Asura_TreeDialog_Item*,bool>* pxCallback) {m_xAdditionalIconClickCallback.SetCallback(pxCallback);}
	#endif
	void SetIconClickCallback(const Asura_Callback_Base_2Arg<void,Asura_TreeDialog_Item*,bool>* pxCallback) {m_xIconClickCallback.SetCallback(pxCallback);}
	void SetSelectionChangedCallback(const Asura_Callback_Base_1Arg<void,Asura_TreeDialog_Item*>* pxCallback) {m_xSelectionChangedCallback.SetCallback(pxCallback);}

protected :
	virtual void Update( const Asura_Vector_2& xOrigin );
	virtual void Render( const Asura_Vector_2& xOrigin );

	void GetItemUnderCursor(const Asura_Vector_2 xOrigin,Asura_TreeDialog_Item*& pxOutputItem, Asura_Tree_LineElement* peElement) const;

private :
	void CallDragDropCallback();

	void RenderIcon(const Asura_Bounding_Rectangle& xRect, float fXOffset, float fYOffset, int iIcon, u_int uColour);

	void RenderDraggedItemNames();

	//the rename timer is used to delay the renaming until it can be determined that a double click isn't coming up
	void StartRenameTimer(Asura_TreeDialog_Item* pxItem);
	void CancelRenameTimer();
	void UpdateRenameTimer();

	static const u_int MAX_TREEVIEW_ADDITIONAL_ICONS = 4;

	Asura_Tree_Dialog* m_pxTreeDialog;

	Asura_TreeDialog_Tree* m_pxTree;
	bool* m_pbSelectionChanged;

	Asura_TreeDialog_Item* m_pxDragStartItem;//item cursor was over at start of drag
	Asura_TreeDialog_Item* m_pxDragTarget;
	bool m_bSelectAndRenameIfNotDrag;
	bool m_bDragConfirmed;

	Asura_TreeDialog_Item* m_pxRenameItem;
	float m_fRenameTimer;

	bool m_bIconDrag;
	int m_iPreviousIconDragIndex;

	Asura_Callback_Wrapper<void> m_xDoubleClickCallback;
	Asura_Callback_Wrapper_1Arg<void,Asura_TreeDialog_Item*> m_xDragDropCallback;
	Asura_Callback_Wrapper_1Arg<int,Asura_TreeDialog_Item*> m_xIconCallback;
	Asura_Callback_Wrapper_2Arg<void,Asura_TreeDialog_Item*,bool> m_xIconClickCallback;
	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	Asura_Callback_Wrapper_2Arg<int,Asura_TreeDialog_Item*,u_int> m_xAdditionalIconCallback;
	Asura_Callback_Wrapper_2Arg<void,Asura_TreeDialog_Item*,bool> m_xAdditionalIconClickCallback;
	#endif
	Asura_Callback_Wrapper_1Arg<void,Asura_TreeDialog_Item*> m_xSelectionChangedCallback;
};

/////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** Asura_TreeDialog_Tree **
**-----------------------*/

//The tree that gets displayed by the tree dialog
class Asura_TreeDialog_Tree
{
public :
	Asura_TreeDialog_Tree();
	virtual ~Asura_TreeDialog_Tree();

	//Creates a new item , and adds it in the top level of the tree
	//szName is the label to use
	//iUserData is an optional integer that can be recovered later
	Asura_TreeDialog_Item* AddItem(const char* szName,int iUserData = 0);
    //As above , but the item is inserted at the top level of the tree and sorted relative to its siblings.
    Asura_TreeDialog_Item* InsertItem(int ( *pfnCompFunc )( const Asura_TreeDialog_Item& xOne, const Asura_TreeDialog_Item& xTwo ),const char* szName,int iUserData = 0);
	//Creates a new item , and adds it beneath the specified parent
	Asura_TreeDialog_Item* AddItem(Asura_TreeDialog_Item* pxParent,const char* szName,int iUserData = 0);
    //As above , but the item is inserted beneath the specified parent and sorted relative to its siblings.
    Asura_TreeDialog_Item* InsertItem(int ( *pfnCompFunc )( const Asura_TreeDialog_Item& xOne, const Asura_TreeDialog_Item& xTwo ),Asura_TreeDialog_Item* pxParent,const char* szName,int iUserData = 0);

	//Adds a folder path, creating the chains of directory nodes if needed
	void AddFolderPath(const char* szName,int iUserData = 0);
	//As above, creating the chain beneath the specified parent
	void AddFolderPath(Asura_TreeDialog_Item* pxParent,const char* szName,int iUserData = 0);
	//Adds a file path, creating the chains of directory nodes if needed, with the filename as the leaf
	Asura_TreeDialog_Item* AddFilePath(const char* szName,int iFileUserData = 0,int iFolderUserData = 0);
	//As above, creating the chain beneath the specified parent
	Asura_TreeDialog_Item* AddFilePath(Asura_TreeDialog_Item* pxParent,const char* szName,int iFileUserData = 0,int iFolderUserData = 0);

	//Deletes an item , including all of its children.
	//The root shouldn't be deleted in this way
	void DeleteItem(Asura_TreeDialog_Item* pxItem);

	//Moves an item to another position in the tree	
	//Gets put at the end of the child list unless a comparison funcition is provided
	void MoveItem(Asura_TreeDialog_Item* pxItem,Asura_TreeDialog_Item* pxNewParent) {MoveItem(NULL, pxItem, pxNewParent);}
	void MoveItem(int ( *pfnCompFunc )( const Asura_TreeDialog_Item& xOne, const Asura_TreeDialog_Item& xTwo ), Asura_TreeDialog_Item* pxItem,Asura_TreeDialog_Item* pxNewParent);


    // Deletes everything in the tree.
    void DeleteAll();

	//Gets the root of the tree. Can be used as the parent for adding top-level items
	Asura_TreeDialog_Item* GetRoot() {return m_pxRoot;}

	//Gets the first item with the given hash-ID in the tree
	Asura_TreeDialog_Item* FindByHash(const Asura_Hash_ID uHash, Asura_TreeDialog_Item* pxStartItem = NULL);
	//Gets the first item with the given name in the tree
	Asura_TreeDialog_Item* FindByName(const char* szName, Asura_TreeDialog_Item* pxStartItem = NULL);
	//Gets the first item with the given user data in the tree
	Asura_TreeDialog_Item* FindByData(int iUserData, Asura_TreeDialog_Item* pxStartItem = NULL);

	//Gets the number of visible items in the tree (based on which have branches have been expanded)
	int GetNumVisibleItems() const;

	//Get the iIndex'th visible item in the tree
	Asura_TreeDialog_Item* GetVisibleItem(int iIndex);

	//Get the current selection. This is the most recently selected item if multiple selection is enabled
	Asura_TreeDialog_Item* GetSelectedItem() const {return m_xSelection.size() ? m_xSelection.last_entry() : 0;}

	//Get the full list of selected items
	const List<Asura_TreeDialog_Item*>& GetSelection() const {return m_xSelection;}

	//Gets the list of selected items , but removes any items that are children/descendents of another selected item
	void GetSelectionRemovingChildren(List<Asura_TreeDialog_Item*>& xOutput) const;

	//Sets the current selection , and expands any parents to make it visible
	void SetSelectedItem(Asura_TreeDialog_Item* pxSelected);

	//Replaces the current selection list
	void SetSelection(const List<Asura_TreeDialog_Item*>& xSelection);

	//Deselects everything
	void ClearSelection() {m_xSelection.empty();}

	//Add/remove a single item from the selection
	void ToggleSelection(Asura_TreeDialog_Item* pxItem);

	//Adds the specified item to the selection , moving it to the end of the selection if already selected
	void AddToSelection(Asura_TreeDialog_Item* pxItem);

	//deselects the specified ite,
	void RemoveFromSelection(Asura_TreeDialog_Item* pxItem);

	//Selects all visible items between the first and last item , replacing the current selection if bKeepCurrentSelection is false
	//The first and last item must be visible themselves
	void SelectMultiple(Asura_TreeDialog_Item* pxFirstItem, Asura_TreeDialog_Item* pxLastItem, bool bKeepCurrentSelection);
	
	//Checks to see whether the given item is part of the selection.
	bool IsSelected(Asura_TreeDialog_Item* pxItem) const {return m_xSelection.contains(pxItem);}

	//Determines whether multiple selection should be enabled within the tree dialog.
	void EnableMultipleSelection(bool bEnable) {m_bAllowMultipleSelection = bEnable;}
	bool IsMultipleSelectionEnabled() const {return m_bAllowMultipleSelection;}

	//returns true if the item is part of this tree
	bool BelongsToTree(Asura_TreeDialog_Item* pxItem) const;

	// Export content of the tree to file.
	bool ExportToTXTFile( Asura_String xFileName );

private :
	//Removes item from tree (with its children) without deleting it
	void RemoveItem(Asura_TreeDialog_Item* pxItem);

	//Inserts item into tree.
	void InsertItem(Asura_TreeDialog_Item* pxItem,Asura_TreeDialog_Item* pxParent);

    //Inserts item into tree using the sort function.
    void InsertItem(int ( *pfnCompFunc )( const Asura_TreeDialog_Item& xOne, const Asura_TreeDialog_Item& xTwo ),Asura_TreeDialog_Item* pxItem,Asura_TreeDialog_Item* pxParent);
	
	List<Asura_TreeDialog_Item*> m_xSelection;
	bool m_bAllowMultipleSelection;

	Asura_TreeDialog_Item* m_pxRoot;
};


/////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** Asura_TreeDialog_Item **
**-----------------------*/
class Asura_TreeDialog_Item
{
	friend class Asura_Tree_Dialog;
	friend class Asura_TreeDialog_Tree;
	friend class Asura_TreeView_Gadget;

private :
	Asura_TreeDialog_Item(const char* szName,int iUserData);
	~Asura_TreeDialog_Item(){};

public :

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	enum XOffsetsEnum
	{
		XOFFSET_ICONS,
		XOFFSET_ADDITIONAL_ICONS,
		XOFFSET_TEXT,

		NUM_XOFFSETS,
	};
	#endif

	bool IsRoot() const {return m_pxParent == 0;}

	void Expand(bool bExpand);
	bool IsExpanded() const {return m_bExpanded;}

	void			SetHash(const Asura_Hash_ID uHash)	{ m_uHash = uHash; }
	Asura_Hash_ID	GetHash(void) const					{ return(m_uHash); }

	void SetName(const char* szName) {m_xName = szName;}
	const char* GetName() const{return m_xName.GetString();}

	// Return name with path included.
	void ConcatenatePath( Asura_String& xPath ) const;

	int GetUserData() const {return m_iUserData;}
	void SetUserData(int iData) {m_iUserData = iData;}

	void* GetUserPointer() const { return m_pUserPointer; }
	void SetUserPointer(void* pUserPointer) { m_pUserPointer = pUserPointer; }

	void SetTreatAsFolder( bool bVal ) { m_bTreatAsFolder = bVal; }
	bool ShouldTreatAsFolder() const { return m_bTreatAsFolder; }

	//Root is at 0 ,so first proper items will have a depth of 1
	int GetDepth() const {return m_iDepth;}
	int GetNumChildren() const {return m_iNumChildren;}

	//Expands all parents to make this item visible
	//This doesn't remove the hidden flag though.
	void MakeVisible();

	//Gets the index of this item ,just considering visible items
	//Returns -1 if not visible
	int GetVisibleIndex() const;
	
	//SetHidden() will hide this , and also prevent any children from being drawn
	void SetHidden(bool bHide);
	//IsHidden() just checks to see whether this item is explicitly hidden , and not
	//whether it is hidden as a result of the parent being hidden
	bool IsHidden() const {return m_bHidden;}

	Asura_TreeDialog_Item* GetChild() {return m_pxFirstChild;}
	const Asura_TreeDialog_Item* GetChild() const {return m_pxFirstChild;}

	Asura_TreeDialog_Item* GetParent() {return m_pxParent;}
	const Asura_TreeDialog_Item* GetParent() const {return m_pxParent;}

	// Can't use GetNextSibling(), because that's a Win32 macro in windowsx.h
	Asura_TreeDialog_Item* GetSibling() {return m_pxNextSibling;}
	const Asura_TreeDialog_Item* GetSibling() const {return m_pxNextSibling;}

	//Colour override is used for special cases where you do not want the default colours to apply.
	u_int GetColourOverride() {return m_uColourOverride;}
	const u_int GetColourOverride() const {return m_uColourOverride;}

	void SetColourOverride(const u_int uColour) {m_uColourOverride = uColour;}

	//Searches for an immediate child with the given name
	Asura_TreeDialog_Item* FindChildByName(const char* szName, bool bIgnoreCase);

	// Help text for the name label.
	//need to tell the tree dialog itself to use these (SetUsePerItemHelp(true))
	void SetHelpText(const char* szText) { m_xHelpText = szText; }
	const char* GetHelpText() const { return m_xHelpText.GetString(); }

	// Help text for the icons.
	//need to tell the tree dialog itself to use these (SetUsePerItemHelp(true))
	void SetIconHelpText(const char* szText) { m_xIconHelpText = szText; }
	const char* GetIconHelpText() const { return m_xIconHelpText.GetString(); }

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	void SetRenameExtents(int iLeftIndex, int iRightIndex) { m_iRenameExtentLeftIndex = iLeftIndex; m_iRenameExtentRightIndex = iRightIndex; }
	void GetRenameExtents(int& iLeftIndex, int& iRightIndex) { iLeftIndex = m_iRenameExtentLeftIndex; iRightIndex = m_iRenameExtentRightIndex; }
	#endif

	//returns true if this item is the specified item , or if it a child , grandchild etc of that item
	bool IsDescendentOf(const Asura_TreeDialog_Item* pxItem) const
	{
		if(this == pxItem) return true;
		return m_pxParent ? m_pxParent->IsDescendentOf(pxItem) : false;
	}

	// It will export content of all names to txt file. (Will return false, if export is unsuccessful.)
	void ExportToTXTFile( FILE* fp, u_int uTabLevel );

private :
	void Delete();

	const Asura_TreeDialog_Item* GetRoot() const {return m_pxParent ? m_pxParent->GetRoot() : this;}
	
	//updates depth value for this and any children.
	void UpdateDepth();


	Asura_TreeDialog_Item* FindByHash(const Asura_Hash_ID uHash);
	Asura_TreeDialog_Item* FindByName(const char* szName);
	Asura_TreeDialog_Item* FindByData(int iUserData);

	bool IsVisibleDescendentCountValid() const {return m_iVisibleDescendents>=0;}
	void UpdateVisibleDescendents() const;
	void InvalidateVisibleDescendents();
	Asura_TreeDialog_Item* GetVisibleItem(int iIndex) const;

	int GetVisibleDescendents() const
	{
		if(!IsVisibleDescendentCountValid()) UpdateVisibleDescendents();
		return m_iVisibleDescendents;
	}

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	int m_iRenameExtentLeftIndex;
	int m_iRenameExtentRightIndex;
	#endif

	#ifndef TOOLS_USE_OLD_ENTITY_MANAGER
	float m_afXOffsets [NUM_XOFFSETS];
	#endif

	Asura_Hash_ID	m_uHash;	// Hash-ID of this item - initialised from the name.
	Asura_String m_xName;
	int m_iUserData;
	void* m_pUserPointer;
	Asura_String m_xHelpText;		// Help text for the name label.
	Asura_String m_xIconHelpText;	// Help text for the icon.

	bool m_bExpanded;
	bool m_bHidden;
	int m_iNumChildren;
	mutable int m_iVisibleDescendents;
	int m_iDepth;
	u_int m_uColourOverride;
	bool m_bTreatAsFolder;

	Asura_TreeDialog_Item* m_pxParent;
	Asura_TreeDialog_Item* m_pxNextSibling;
	Asura_TreeDialog_Item* m_pxFirstChild;
	Asura_TreeDialog_Item* m_pxLastChild;

};


////////////////////////////////////////////////////////////////////////////////
// Class        : Asura_TreeDialog_FileTree
// Author       : Andy Tate
// Date         : 11-Dec-2008
//
// Description  : Same as the standard tree-dialogue except that it automatically
//				  handles and displays directories and filenames.
////////////////////////////////////////////////////////////////////////////////

class Asura_TreeDialog_FileTree : public Asura_TreeDialog_Tree
{
public:
	// Public functions...
	Asura_TreeDialog_FileTree(const char* szRootDirectory, const char* szFileExtension, const bool bAllowNone = true )
	{
		PopulateTree(szRootDirectory, szFileExtension,  bAllowNone);
	}
	//virtual ~Asura_TreeDialog_FileTree();

	// Will populate any Asura_TreeDialog_Tree& xTree, regardless of where it exists. Usefull for all classes that needs to fill Tree object with content of directories.
	void	PopulateTree(const char* szRootDirectory, const char* szFileExtension, const bool bAllowNone = true );

	Asura_TreeDialog_Item*	AddItem(const Asura_String& xPath);

	// Helper function (that should be probably moved to some other place later on). It scan any pxTree and it locate one which its name and path correspond to the hash id we are looking for.
	Asura_TreeDialog_Item*	FindTreeItemByHashWithPath( Asura_TreeDialog_Item* pxRootItem, Asura_Hash_ID uSearchItemHashID );

	bool	IsCurrentItemAFile(void) const		{ const Asura_TreeDialog_Item* pxCurrentItem = GetSelectedItem(); return((pxCurrentItem == NULL) ? false: IsItemAFile(*pxCurrentItem)); }
	bool	IsCurrentItemADirectory(void) const	{ const Asura_TreeDialog_Item* pxCurrentItem = GetSelectedItem(); return((pxCurrentItem == NULL) ? false: IsItemADirectory(*pxCurrentItem)); }
	bool	IsCurrentItemUnset(void) const		{ const Asura_TreeDialog_Item* pxCurrentItem = GetSelectedItem(); return((pxCurrentItem == NULL) ? true : IsItemUnset(*pxCurrentItem)); }


protected:
	// Protected variables...

	// Protected functions...


private:
	// Private typedefs...
	typedef Asura_TreeDialog_Tree PARENT;

	// Private enumerated types...
	enum
	{
		UNSET_ITEM		= 0x00000000,
		DIRECTORY_ITEM	= 0x00000001,
		FILE_ITEM		= 0x00000002,
	};

	// Private variables...

	// Private functions...
	Asura_TreeDialog_FileTree(void);
	Asura_TreeDialog_FileTree(const Asura_TreeDialog_FileTree& xOther);

	Asura_TreeDialog_FileTree&	operator=(const Asura_TreeDialog_FileTree& xOther);

	Asura_TreeDialog_Item*	CreateItem(const Asura_String& xPath);

	bool	IsItemAFile(const Asura_TreeDialog_Item& xItem) const;
	bool	IsItemADirectory(const Asura_TreeDialog_Item& xItem) const;
	bool	IsItemUnset(const Asura_TreeDialog_Item& xItem) const;

};   // End of class 'Asura_TreeDialog_FileTree'.


#endif //__ASURA_TREE_DIALOG_H__
