UI/ReplayReactionOverlay.razor
@using System
@using Sandbox
@using Sandbox.UI
@inherits Panel
@namespace BlockParty
@attribute [StyleSheet( "ReplayReactionOverlay.razor.scss" )]

<root>
	@if ( Visible && Manager.CurrentReplayReactions is { } reactions )
	{
		@if ( Manager.IsPlacingReaction && Manager.CanReact )
		{
			<div class="placement-catcher" style=@PlayfieldStyle() onclick=@(() => Manager.PlaceReaction())></div>
		}
		@for ( int order = 0; order < reactions.OrderedEntries.Count; order++ )
		{
			var (steamId, reaction) = reactions.OrderedEntries[order];
			@if ( reaction.IsVisibleAt( Manager.ReplayFrame ) )
			{
				var effect = ReplayReactionEffects.Find( reaction.Effect );
				var layout = ReplayReactionDisplay.At( effect, new Vector2( reaction.X, reaction.Y ), Manager.ReplayFrame - reaction.Frame );
				<div @key="steamId" class="reaction @(Manager.IsPlacingReaction ? "passive" : "")" style=@PairStyle( layout, order )>
					<div class="avatar" style=@AvatarStyle() onmouseover=@Tip( steamId )></div>
					<div class="effect" style=@EffectStyle( effect ) onmouseover=@Tip( steamId )></div>
				</div>
			}
		}
		@if ( Manager.IsPlacingReaction && Manager.CanReact && Manager.ReactionCursorPosition( out var cursor ) )
		{
			var effect = ReplayReactionEffects.Find( Manager.ReactionEffect );
			var layout = ReplayReactionDisplay.At( effect, cursor, 0, preview: true );
			<div class="reaction passive" style=@PairStyle( layout, reactions.OrderedEntries.Count )>
				<div class="avatar" style=@AvatarStyle()></div>
				<div class="effect" style=@EffectStyle( effect )></div>
			</div>
		}
	}
</root>

@code
{
	public GameStage Stage { get; set; }
	private GameManager Manager => GameManager.Instance;
	private bool Visible => ReplayReactionDisplay.IsVisible( Manager, Stage );
	private Panel _hovered;
	private long _hoveredId;
	private Action<PanelEvent> Tip( long id ) => e => { _hovered = e.This; _hoveredId = id; };
	public override void Tick()
	{
		base.Tick();
		if ( Visible ) Stage.EnsureReactionSprites();
		bool hasReaction = Visible && !Manager.IsPlacingReaction && _hovered.IsValid() && _hovered.HasHovered
			&& Manager.CurrentReplayReactions?.Entries.TryGetValue( _hoveredId, out var reaction ) == true
			&& reaction.IsVisibleAt( Manager.ReplayFrame );
		if ( hasReaction ) Stage?.ShowReactionTooltip( Manager.CurrentReplayReactions.NameFor( _hoveredId ) );
		else Stage?.ReactionTooltip?.Hide();
	}
	private float PixelScale => Screen.Height * ScaleFromScreen / Manager.Camera.OrthographicHeight;
	private string PlayfieldStyle()
	{
		var screen = Manager.Camera.PointToScreenNormal( new Vector3( Arena.WALL_SIZE, Arena.HEIGHT - Arena.WALL_SIZE, 0f ) );
		return FormattableString.Invariant( $"left:{screen.x * Screen.Width * ScaleFromScreen:0.###}px;top:{screen.y * Screen.Height * ScaleFromScreen:0.###}px;width:{(Arena.WIDTH - 2 * Arena.WALL_SIZE) * PixelScale:0.###}px;height:{(Arena.HEIGHT - 2 * Arena.WALL_SIZE) * PixelScale:0.###}px;" );
	}
	private string PairStyle( ReplayReactionDisplay.Layout layout, int order )
	{
		var screen = Manager.Camera.PointToScreenNormal( new Vector3( layout.Left, layout.Top, 0f ) );
		return FormattableString.Invariant( $"left:{screen.x * Screen.Width * ScaleFromScreen:0.###}px;top:{screen.y * Screen.Height * ScaleFromScreen:0.###}px;width:{layout.Width * PixelScale:0.###}px;height:{layout.Height * PixelScale:0.###}px;opacity:{layout.Alpha:0.###};gap:{ReplayReactionDisplay.Gap * PixelScale:0.###}px;z-index:{order + 1};" );
	}
	private string EffectStyle( ReplayReactionEffect effect ) => FormattableString.Invariant(
		$"width:{effect.Width * PixelScale:0.###}px;height:{effect.Height * PixelScale:0.###}px;" );
	private string AvatarStyle() => FormattableString.Invariant(
		$"width:{ReplayReactionDisplay.AvatarSize * PixelScale:0.###}px;height:{ReplayReactionDisplay.AvatarSize * PixelScale:0.###}px;" );
	protected override int BuildHash() => HashCode.Combine( Visible, Manager?.ReplayFrame ?? 0,
		Manager?.CurrentReplayReactions?.Revision ?? 0, Manager?.IsPlacingReaction ?? false,
		HashCode.Combine( Manager?.ReactionEffect, Manager?.CanReact ?? false ), Manager?.IsPlacingReaction == true ? Mouse.Position : Vector2.Zero,
		Screen.Size, Manager?.Camera?.OrthographicHeight ?? 0f );
}