//==============================================================================
//	Created on: 10/12/2003
//	A user-configured list of streaming music files.
//
//	Written by Ron Prestenback
//	© 2003, Epic Games, Inc.  All Rights Reserved
//==============================================================================
class StreamPlaylist extends StreamBase
	PerObjectConfig
	Config(UPlaylists);

var() protected config int           Current;
var() protected config array<string> Playlist;
var() protected string               Title;

var() protected config bool          bNeedSave;
var() editinline editconst noexport private   editconstarray transient array<int>    RandomPool;
var() editinline editconst noexport protected StreamInterface                        FileManager;
var() editinline editconst noexport protected editconstarray           array<Stream> Songs;

delegate OnPlaylistChanged();

function bool InitializePlaylist( StreamInterface InManager )
{
	if ( InManager == None || IsInitialized() )
		return false;

	FileManager = InManager;
	LoadPlaylist();
	return true;
}

function bool LoadPlaylist()
{
	local int i;

	ClearPlaylist();
	for ( i = 0; i < Playlist.Length; i++ )
	{
		if ( !FileManager.ValidFile(Playlist[i]) )
		{
			log("Removing file from playlist '"$GetTitle()$"' - file not found",'MusicPlayer');
			Playlist.Remove(i--,1);
			continue;
		}

		AddSong(Playlist[i]);
	}

	ResetRandomPool();
	bNeedSave = False;
	return true;
}

function Stream CreateStream( string FileName )
{
	if ( FileName == "" )
		return None;

	return FileManager.CreateStream( FileName );
}

function int AddSong( string SongFileName, optional bool bSkipNotification )
{
	local Stream NewStream;
	local EFileType Type;
	local int i;

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

	Type = ConvertToFileType(SongFileName);
	if ( Type == FILE_Stream )
	{
		NewStream = CreateStream(SongFileName);
		if ( NewStream == None )
			return -1;

		i = AddStream(GetPlaylistLength(), NewStream, bSkipNotification);
		bNeedSave = bNeedSave || ValidStreamIndex(i);

		return i;
	}

	return -1;
}

function int AddStream( int Index, Stream NewStream, optional bool bSkipNotification )
{
	local int i;
	local string Str;

	if ( NewStream == None )
		return -1;

	Str = NewStream.GetFileName();
	if ( Str == "" )
		return -1;

	i = FindIndexByName( Str );
	if ( !ValidStreamIndex(i) )
	{
		if ( !ValidStreamIndex(Index) )
			Index = GetPlaylistLength();

		Songs.Insert( Index, 1 );
		Songs[Index] = NewStream;
		RandomPool[RandomPool.Length] = Index;

		if ( !bSkipNotification )
			OnPlaylistChanged();
		return Index;
	}

	return i;
}

function int InsertSong( int idx, string Path, optional bool bSkipNotification )
{
	local Stream NewStream;
	local EFileType Type;
	local int i;

	if ( !ValidStreamIndex(idx) )
		return AddSong(Path,bSkipNotification);

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

	Type = ConvertToFileType(Path);
	if ( Type == FILE_Stream )
	{
		NewStream = CreateStream(Path);
		if ( NewStream == None )
			return -1;

		i = AddStream(idx, NewStream, bSkipNotification);
		bNeedSave = bNeedSave || ValidStreamIndex(i);
		return i;
	}

	return -1;

}

function bool RemoveSong( string SongFileName, optional bool bSkipNotification )
{
	return RemoveSongAt(FindIndexByName(SongFileName), bSkipNotification);
}

function bool RemoveSongAt( int idx, optional bool bSkipNotification )
{
	local int i;

	if ( !ValidStreamIndex(idx) )
		return false;

	Songs.Remove(idx,1);

	// Remove the last index from the random pool
	for ( i = 0; i < RandomPool.Length; i++ )
		if ( RandomPool[i] == Songs.Length )
		{
			RandomPool.Remove(i,1);
			break;
		}

	// If this was the current song, attempt to set the current to the previous song, so that the playlist will
	// select the next song correctly
	if ( idx == Current && !SetCurrent(idx-1) )
		SetCurrent(0);

	bNeedSave = True;

	if ( !bSkipNotification )
		OnPlaylistChanged();
	return true;
}

function string NextSong( bool bMayRepeat )
{
	local int i;

	i = GetCurrent() + 1;
	if ( !ValidStreamIndex(i) && bMayRepeat )
		i = 0;

	if ( SetCurrent(i) )
		return Songs[Current].GetPath();

	return "";
}

function string PrevSong( bool bMayRepeat )
{
	local int i;

	i = GetCurrent() - 1;
	if ( !ValidStreamIndex(i) && bMayRepeat )
		i = GetPlaylistLength() - 1;

	if ( SetCurrent(i) )
		return Songs[Current].GetPath();

	return "";
}

function ReplaceWith( StreamPlaylist Other )
{
	if ( Other == None )
		return;

	FileManager = Other.FileManager;
	Other.GetSongs(Songs);
	ResetRandomPool();

	SetCurrent( Other.GetCurrent() );
	bNeedSave = True;
}

function bool ClearPlaylist()
{
	bNeedSave = bNeedSave || Songs.Length > 0;
	Songs.Remove( 0, Songs.Length );
	RandomPool.Remove(0, RandomPool.Length);

	return true;
}

function string GetRandomSong()
{
	local int idx;
	local Stream RandomStream;

	if ( RandomPool.Length == 0 )
		ResetRandomPool();

	if ( RandomPool.Length > 0 )
	{
		idx = Rand(RandomPool.Length - 1);
		if ( ValidStreamIndex(RandomPool[idx]) )
		{
			RandomStream = Songs[RandomPool[idx]];
			RandomPool.Remove(idx,1);
			return RandomStream.GetPath();
		}
	}

	return "";
}

function ResetRandomPool()
{
	local int i;

	RandomPool.Length = Songs.Length;
	for ( i = 0; i < Songs.Length; i++ )
		RandomPool[i] = i;
}

function bool SetTitle( string NewTitle )
{
	if ( NewTitle == "" )
		return false;

	Title = NewTitle;
	OnPlaylistChanged();
	Save();

	return true;
}

function bool SetCurrent( int Index )
{
	if ( !ValidStreamIndex(Index) )
		return false;

	Current = Index;
	bNeedSave = True;
	return true;
}

// DOES NOT perform notification!!
function bool SetSongs( array<Stream> NewSongs )
{
	Songs = NewSongs;
	ResetRandomPool();
	bNeedSave = True;
	return true;
}

// =====================================================================================================================
// =====================================================================================================================
// Query
// =====================================================================================================================
// =====================================================================================================================

function bool IsInitialized()
{
	return FileManager != None;
}

function string GetTitle()
{
	return Title;
}

function int GetCurrent()
{
	return Current;
}

function GetSongs( out array<Stream> SongArray )
{
	SongArray = Songs;
}

function Stream GetCurrentStream()
{
	return GetStreamAt(GetCurrent());
}

function Stream GetStream( string StreamName )
{
	local int i;

	i = FindIndexByName(StreamName);
	return GetStreamAt(i);
}

function Stream GetStreamAt( int Index )
{
	if (ValidStreamIndex(Index) )
		return Songs[Index];

	return None;
}

function int GetPlaylistLength()
{
	return Songs.Length;
}

function int FindIndexByName( string Test )
{
	local FilePath APath;

	if ( ParsePath(Test, APath) )
	{
		if ( APath.Extension != "" )
		{
			if ( APath.Directory != "" )
				return FindIndexByPath(APath.FullPath);
			else if ( APath.FileName != "" )
				return FindIndexByFullName( APath.FileName $ "." $ APath.Extension );
		}
		else return FindIndexByFileName(APath.FileName);
	}

	return -1;
}

function int FindIndexByPath( string Test )
{
	local int i;

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

	for ( i = 0; i < Songs.Length; i++ )
		if ( Songs[i].GetPath() ~= Test )
			return i;

	return -1;
}

function int FindIndexByFullName( string Test )
{
	local int i, num;

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

	num = GetPlaylistLength();
	for ( i = 0; i < num; i++ )
	{
		if ( Songs[i].GetFullName() ~= Test )
			return i;
	}

	return -1;
}

function int FindIndexByFileName( string Test )
{
	local int i, num;

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

	num = GetPlaylistLength();
	for ( i = 0; i < num; i++ )
	{
		if ( Songs[i].GetFileName() ~= Test )
			return i;
	}

	return -1;
}

function bool ValidStreamName( string FileName )
{
	if ( FileName == "" )
		return false;

	return ValidStreamIndex( FindIndexByName(FileName) );
}

function bool ValidStreamIndex( int Index )
{
	return Index >= 0 && Index < Songs.Length;
}

// =====================================================================================================================
// =====================================================================================================================
//  Debug
// =====================================================================================================================
// =====================================================================================================================

function DebugInfo()
{
	local int i;

	log("   Playlist '"$GetTitle()$"', Length:"$GetPlaylistLength(),'MusicPlayer');
	for ( i = 0; i < GetPlaylistLength(); i++ )
		log("    "$i$")"@Songs[i].GetSongTitle(),'MusicPlayer');
}

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

	if ( Super.HandleDebugExec(Command,Param) )
		return true;

	switch ( Locs(Command))
	{
	case "addsong":
		AddSong(Param);
		return true;

	case "dumptags":
		for ( i = 0; i < Songs.Length; i++ )
			Songs[i].DumpTags();
		return true;

	case "scripttags":
		for ( i = 0; i < Songs.Length; i++ )
			Songs[i].DumpScriptTag();

	case "showsongs":
		log("Playlist"@GetTitle()@"has"@Songs.Length@"songs",'MusicPlayer');
		for ( i = 0; i < Songs.Length; i++ )
			log("  "$i$")"@Songs[i].GetPath(),'MusicPlayer');

		return true;
	}

	return false;
}

function Save()
{
	local int i;

	if ( !bNeedSave )
		return;

	Playlist.Remove(0, Playlist.Length);
	for ( i = 0; i < Songs.Length; i++ )
		if ( Songs[i] != None )
			Playlist[i] = Songs[i].GetPath();

	bNeedSave = False;
	SaveConfig();
}


DefaultProperties
{
	bNeedSave=True
}
