//==============================================================================
//	Created on: 10/21/2003
//	File System Object
//
//	Written by Ron Prestenback
//	© 2003, Epic Games, Inc.  All Rights Reserved
//==============================================================================
class StreamDirectoryNode extends StreamBase
	within DirectoryTreeList;

var() StreamDirectoryNode        Parent;
var() editconst editinline array<StreamDirectoryNode> Children;
var() editconst editinline array<string>              Contents;

var()           private int    NodeRenderCost, ChildRenderCost;
var() editconst private string MyPath;
var() editconst private string DirectoryName;
var()           private bool   bExpanded;
var() private bool   bInitialized;

function InitializeNode()
{
	if ( Initialized() )
		return;

	Clear();
	bInitialized = True;

	// Maybe root
	if ( Parent == None || MyPath == "" || MyPath == ".." )
		return;

	if ( bSimpleFileBrowsing )
		AddChild("..").InitializeNode();

	RefreshNodeContents();
}

function RefreshNodeContents()
{
	local int i;
	local array<string> Directories, Files;

	if ( FileManager == None )
	{
		log("Cannot refresh node '"$MyPath$"' contents - File Manager not set.",'MusicPlayer');
		return;
	}

	if ( HasChildren() )
	{
		for ( i = Children.Length - 1; i >= 0; i-- )
			if ( Children[i].GetPath() != ".." )
				Children.Remove(i,1);
	}

	Contents.Remove(0, Contents.Length);

	FileManager.GetDirectoryContents( Directories, MyPath, FILE_Directory );
	for ( i = 0; i < Directories.Length; i++ )
		AddChild(Directories[i]);

	FileManager.GetDirectoryContents( Files, MyPath, FILE_Stream );
	FileManager.GetDirectoryContents( Files, MyPath, FILE_Playlist );
	for ( i = 0; i < Files.Length; i++ )
		AddContent(Files[i]);

	UpdateCost();
}

function Clear( optional bool bPropagate )
{
	local int i;

//	log(GetName()@"Clear");
	if ( HasChildren() && bPropagate )
	{
		for ( i = 0; i < Children.Length; i++ )
			Children[i].Clear(bPropagate);
	}

	Children.Remove(0, Children.Length);
	Contents.Remove(0, Contents.Length);
	UpdateCost();

	bInitialized = False;
}

function string GetIndent()
{
	local string Indent;

	if ( bSimpleFileBrowsing )
		return "";

	if ( Parent != None )
		Indent = Parent.GetIndent() $ " ";

	return Indent;
}

function string GetNodePrefix( bool bNoPrefix )
{
	if ( bNoPrefix )
		return "";

	if ( IsEmpty() )
		return GetIndent() $ ". ";

	if ( !IsOpen() )
		return GetIndent() $ "+ ";

	return GetIndent() $ "- ";
}

function string NodeText( int VisibleIndex, optional bool bNoPrefix )
{
	local StreamDirectoryNode Node;

	if ( !IsOpen() )
		return GetNodePrefix(bNoPrefix) $ DirectoryName;

	Node = FindVisibleNode(VisibleIndex);
	if ( Node == Self )
	{
		if ( VisibleIndex >= 0 && VisibleIndex < Contents.Length )
			return Contents[VisibleIndex];

		return GetNodePrefix(bNoPrefix) $ DirectoryName;
	}

	return Node.NodeText(VisibleIndex,bNoPrefix);
}

function StreamDirectoryNode FindVisibleNode( out int idx )
{
	local int count, i;

	if ( !IsOpen() )
		return Self;

	InitializeNode();
	if ( idx > 0 )
	{
		count = Children[0].Cost();
		if ( HasChildren() )
		{
			while ( count <= idx && ++i < Children.Length )
				count += Children[i].Cost();
		}

		if ( i == Children.Length || !HasChildren() )
		{
			idx -= count;
			return Self;
		}

		idx = count - idx;
	}

	return Children[i].FindVisibleNode( idx );
}

function int FindVisibleNodeIndex( string Path )
{
	local int i, VisibleIndex;
	local string MyComponent;

	if ( Path == "" )
		return 1;

	InitializeNode();
	MyComponent = StripPathComponent(Path);
	if ( MyComponent == "" )
		MyComponent = Path;

	VisibleIndex = 1;
	for ( i = 0; i < Children.Length; i++ )
	{
		if ( CompareNames(MyComponent, Children[i].GetName()) )
			return VisibleIndex + Children[i].FindVisibleNodeIndex(Path);

		VisibleIndex += Children[i].Cost();
	}

	for ( i = 0; i < Contents.Length; i++ )
	{
		if ( CompareNames(MyComponent, Contents[i]) )
			break;

		VisibleIndex++;
	}

	return VisibleIndex;
}

function int Cost()
{
	return NodeRenderCost + ChildRenderCost;
}

function UpdateCost( optional bool bFullUpdate )
{
	local int i;

	NodeRenderCost = 0;
	ChildRenderCost = 0;

	if ( !IsOpen() )
		NodeRenderCost = 1;

	else
	{
		if ( !bSimpleFileBrowsing )
			NodeRenderCost = 1;

		for ( i = 0; i < Children.Length; i++ )
		{
			if ( bFullUpdate )
				Children[i].UpdateCost( bFullUpdate );

			ChildRenderCost += Children[i].Cost();
		}

		NodeRenderCost += Contents.Length;
	}
}

function Toggle()
{
	if ( IsOpen() )
		Collapse();

	else Expand();
}

function Expand( optional bool bFullUpdate )
{
	InitializeNode();

	bExpanded = True;
	UpdateCost(bFullUpdate);
}

function Collapse( optional bool bFullUpdate )
{
	InitializeNode();

	bExpanded = False;
	UpdateCost(bFullUpdate);
}

function bool ChangeDirectory( string Path, out StreamDirectoryNode Node, optional bool bRefreshContents )
{
	local int i;
	local string MyComponent;

	InitializeNode();

//	log(GetName()@"ChangeDirectory to '"$Path$"'",'MusicPlayer');
	if ( Path == "" || Path == "." )
	{
		if ( bRefreshContents && Initialized() )
			RefreshNodeContents();

		if ( IsEmpty() )
		{
			Node = None;
			return false;
		}

		Expand();
		Node = Self;
		return true;
	}

	if ( Path == ".." )
	{
		if ( Parent == None )
		{
			Expand();
			Node = Self;
			return true;
		}

		Collapse();
		return Parent.ChangeDirectory("",Node,bRefreshContents);
	}

	MyComponent = StripPathComponent(Path);
	if ( MyComponent == "" )
	{
		MyComponent = Path;
		Path = "";
	}

	i = FindChildIndex(MyComponent);
	if ( Valid(i) )
	{
		if ( Children[i].ChangeDirectory(Path, Node, bRefreshContents) )
		{
			Collapse();
			return true;
		}
	}

	Node = None;
	return false;
}

function bool ExpandPath( string Path )
{
	local int i, idx;
	local string s;

	InitializeNode();
	if ( Path == "" )
	{
		Expand();
		return true;
	}

	s = StripPathComponent(Path);
	if ( s == "" )
		s = Path;

	if ( s != "" )
	{
		idx = FindChildIndex(s);
		if ( Valid(idx) )
		{
			for ( i = 0; i < Children.Length; i++ )
			{
				if ( Children[i] != Children[idx] )
					Children[i].Collapse();
			}

			if ( Path != "" )
				Children[idx].ExpandPath(Path);
		}

		Expand();
		return true;
	}

	return false;
}

function StreamDirectoryNode AddChild( string ChildName )
{
	local int i;
	local string s;
	local StreamDirectoryNode Child;

	if ( ChildName == "" )
		return None;

	InitializeNode();

//	log(DirectoryName@"adding directory:"@ChildName,'MusicPlayer');
	while ( i < Children.Length )
	{
		s = Children[i].GetName();
		if ( CompareNames(s, ChildName) )
			return Children[i];

		if ( ChildName < s )
			break;

		i++;
	}

	Child = CreateChild(ChildName);
	if ( Child == None )
	{
		log(GetName()@"failed to successfully create child '"$ChildName$"'",'MusicPlayer');
		return None;
	}

	Children.Insert( i, 1 );
	Children[i] = Child;
	UpdateCost();

	return Child;
}

function bool AddContent( string ContentName )
{
	local int i;
	local string s;

	if ( ContentName == "" )
		return false;

	InitializeNode();

	s = StripPathComponent(ContentName);
	if ( s != "" )
	{
		i = FindChildIndex(s);
		if ( Valid(i) )
			return Children[i].AddContent(ContentName);

		return false;
	}

	i = FindFileIndex(ContentName);
	if ( i == -1 )
		Contents[Contents.Length] = ContentName;

	return true;
}

function bool RemoveChild( StreamDirectoryNode Child )
{
	local int i;

	if ( Child == None )
		return false;

	InitializeNode();
	i = FindChildIndex(Child.GetName());
	return RemoveChildAt(i);
}

function bool RemoveChildAt( int i )
{
	if ( Valid(i) )
	{
		Children.Remove(i,1);
		UpdateCost(True);
		return true;
	}

	return false;
}

function bool RemoveContent( string Path )
{
	local string s;
	local int i;

	InitializeNode();

	s = StripPathComponent(Path);
	if ( s != "" )
	{
		i = FindChildIndex(s);
		if ( Valid(i) )
			return Children[i].RemoveContent(Path);

		return false;
	}

	i = FindFileIndex(Path);
	return RemoveContentAt(i);
}

function bool RemoveContentAt( int i )
{
	if ( ValidFile(i) )
	{
		Contents.Remove(i,1);
		UpdateCost(True);
		return true;
	}

	return false;
}

function int FindFileIndex( string FileName )
{
	local int i;

	if ( FileName == "" )
		return -1;

	InitializeNode();

	for ( i = 0; i < Contents.Length; i++ )
		if ( CompareNames(Contents[i], FileName) )
			return i;

	return -1;
}

function bool SetName( string InName )
{
	if ( InName == "" )
		return false;

	DirectoryName = InName;
	return true;
}

function bool SetParent( StreamDirectoryNode InParent )
{
	if ( InParent == None || Parent != None )
		return false;

	Parent = InParent;
	return true;
}

function bool SetPath( string InPath )
{
	if ( MyPath != "" )
		return false;

	if ( DirectoryName == ".." )
	{
		MyPath = DirectoryName;
		return true;
	}

	MyPath = InPath $ DirectoryName $ GetPathSeparator();
	return true;
}

function bool Initialized()
{
	return bInitialized;
}

function bool IsEmpty()
{
	return Initialized() && !HasChildren() && Contents.Length == 0;
}

function bool IsOpen()
{
	return bExpanded;
}

function string GetName()
{
	return DirectoryName;
}

function string GetPath()
{
	return MyPath;
}

function StreamDirectoryNode FindChildByPath( out string Path )
{
	local string ChildName;
	local int i;

	InitializeNode();
	if ( Path == "" )
		return Self;

	ChildName = StripPathComponent(Path);
	if ( ChildName == "" )
		return Self;

	i = FindChildIndex(ChildName);
	if ( Valid(i) )
		return Children[i].FindChildByPath(Path);

	return None;
}

function StreamDirectoryNode FindChild( string ChildName, optional bool bDeepSearch )
{
	local int i;
	local StreamDirectoryNode Node;

	InitializeNode();
	for ( i = 0; i < Children.Length; i++ )
	{
		if ( CompareNames(Children[i].GetName(), ChildName) )
			return Children[i];

		if ( bDeepSearch )
		{
			Node = Children[i].FindChild(ChildName, bDeepSearch);
			if ( Node != None )
				return Node;
		}
	}

	return None;
}

function int FindChildIndex( string ChildName )
{
	local int i;

	if ( ChildName == "" )
		return -1;

	InitializeNode();

//	log(GetName()@"FindChildIndex ChildName '"$ChildName$"'",'MusicPlayer');
	if ( Right(ChildName,1) == GetPathSeparator() )
		ChildName = Left(ChildName, Len(ChildName) - 1);

	for ( i = 0; i < Children.Length; i++ )
		if ( CompareNames(Children[i].GetName(), ChildName) )
			return i;

	return -1;
}


function bool HasChildren()
{
	if ( bSimpleFileBrowsing )
		return Children.Length > 1;

	return Children.Length > 0;
}

function bool Valid( int i )
{
	return i >= 0 && i < Children.Length && Children[i] != None;
}

function bool ValidFile( int i )
{
	return i >= 0 && i < Contents.Length;
}

// =====================================================================================================================
//
//    Internal
//
// =====================================================================================================================

protected function StreamDirectoryNode CreateChild( string ChildName )
{
	local StreamDirectoryNode Child;

	if ( ChildName == "" )
		return None;

	Child = new(Outer) class'GUI2K4.StreamDirectoryNode';
	Assert(Child != None);

	Child.SetParent( Self );
	Child.SetName( ChildName );
	Child.SetPath( MyPath );
	return Child;
}

static function string StripPathComponent( out string Path )
{
	local string s;
	local int i;

	if ( Path == "" )
		return "";

	i = InStr(Path, GetPathSeparator());
	if ( i == -1 )
		return "";

	s = Left(Path,i);
	Path = Mid(Path,i+1);
	return s;
}

function bool HandleDebugExec( string Command, string Param )
{
	local int i;
	local StreamDirectoryNode Node;

	switch ( Command )
	{
	case "cwd":
		log("Current Directory:"$GetPath(),'MusicPlayer');
		return true;

	case "dumpcwd":
		log("Dumping directory contents for"@GetName(),'MusicPlayer');
		for ( i = 0; i < Children.Length; i++ )
			log(" Child "$i@"'"$Children[i].GetName()$"'",'MusicPlayer');
		for ( i = 0; i < Contents.Length; i++ )
			log(" Content "$i@"'"$Contents[i]$"'",'MusicPlayer');

		return true;

	case "cost":
		if ( Param == "" )
			Node = Self;
		else
		{
			Node = FindChild(Param);
			if ( Node == None )
			{
				log("Couldn't find child '"$Param$"' in directory"@GetName(),'MusicPlayer');
				return true;
			}
		}

		log("Cost for directory '"$Node.GetName()$"': "$Node.Cost()@"Open:"$Node.IsOpen()@"Initialized:"$Node.Initialized(),'MusicPlayer');
		return true;
	}

	return false;
}

DefaultProperties
{
	NodeRenderCost=1
}
