Code/MediaSpeaker.razor
@using System
@using Sandbox
@using Sandbox.UI
@namespace Bimp
@inherits PanelComponent
@implements Component.ExecuteInEditor

@*
	A small display for audio-only players (speakers, radios, jukeboxes).
	Put this on a GameObject with a WorldPanel (PanelSize ~800x400).
*@

<root class="mediaspeaker @(Player?.HasMedia == true ? "playing" : "idle")">

	<div class="header">
		<i>@(Player?.HasMedia == true ? "graphic_eq" : "speaker")</i>
		@if ( ShowUseHint )
		{
			<div class="title hint"><span>Press</span><InputGlyph Action="use"></InputGlyph><span>to play something</span></div>
		}
		else
		{
			<ScrollingText class="title" Text="@TitleText"></ScrollingText>
		}
	</div>

	<SpectrumBars Player="@Player"></SpectrumBars>

	<div class="footer">
		<div class="time">@TimeText</div>
		@if ( Player is not null && Player.Queue.Count > 0 )
		{
			<div class="queue"><i>queue_music</i> @Player.Queue.Count</div>
		}
	</div>

	@if ( !string.IsNullOrWhiteSpace( Player?.LocalNotice ) )
	{
		<div class="notice">@Player.LocalNotice</div>
	}

</root>

@code
{
	/// <summary>
	/// The player this display shows. Found automatically in this object or its parents if unset.
	/// </summary>
	[Property] public MediaPlayer Player { get; set; }

	/// <summary>
	/// Scale the UI to fit the WorldPanel's PanelSize instead of getting cut off. Sets the WorldPanel's RenderScale.
	/// </summary>
	[Property, Group( "Layout" )] public bool FitToPanel { get; set; } = true;

	/// <summary>
	/// The panel size the UI is designed for.
	/// </summary>
	[Property, Group( "Layout" ), ShowIf( nameof( FitToPanel ), true )] public Vector2 DesignSize { get; set; } = new Vector2( 560, 280 );

	string TitleText
	{
		get
		{
			if ( Player is null ) return "";
			if ( Player.HasMedia ) return Player.Title;
			if ( !string.IsNullOrWhiteSpace( Player.Status ) ) return Player.Status;
			return "";
		}
	}

	/// <summary> Nothing playing or happening: show how to start something, with the USE button's glyph. </summary>
	bool ShowUseHint => Player is not null && !Player.HasMedia && string.IsNullOrWhiteSpace( Player.Status );

	string TimeText
	{
		get
		{
			if ( Player is null || !Player.HasMedia ) return "";
			if ( Player.IsLive ) return "Live";
			var cur = MediaSource.FormatTime( Player.CurrentTime );
			var s = Player.MediaDuration > 0 ? $"{cur} / {MediaSource.FormatTime( Player.MediaDuration )}" : cur;
			return Player.Paused ? $"{s}  (paused)" : s;
		}
	}

	protected override void OnUpdate()
	{
		if ( FitToPanel ) PanelFit.Apply( this, DesignSize );
		if ( Scene.IsEditor ) return;

		Player ??= GetComponentInParent<MediaPlayer>();
	}

	protected override int BuildHash()
	{
		if ( Player is null ) return 0;
		return HashCode.Combine( Player.PlayId, Player.Title, Player.Status, Player.Paused, (int)Player.CurrentTime, Player.Queue.Count, Player.LocalNotice );
	}
}