InteractiveComputer/UI/ComputerAppHost.razor
@namespace PaneOS.InteractiveComputer
@using System
@using PaneOS.InteractiveComputer
@using Microsoft.AspNetCore.Components
@using Sandbox
@using Sandbox.UI
@inherits Panel

@code
{
	[Parameter] public ComputerRunningApp? Instance { get; set; }
	private string? mountedInstanceId;
	private ComputerAppSession? mirrorSession;

	protected override int BuildHash()
	{
		unchecked
		{
			var hash = 17;
			hash = hash * 31 + (Instance?.State.InstanceId?.GetHashCode() ?? 0);
			hash = hash * 31 + (Instance?.State.Data.Count ?? 0);
			hash = hash * 31 + (Instance?.Session?.GetHashCode() ?? 0);
			hash = hash * 31 + (mirrorSession?.GetHashCode() ?? 0);
			return hash;
		}
	}

	protected override void OnAfterTreeRender( bool firstTime )
	{
		try
		{
			base.OnAfterTreeRender( firstTime );
			AddClass( "app-host" );

			if ( Instance?.State is null )
				return;

			var content = ResolveMountedContent();
			if ( !content.IsValid() )
				return;

			if ( ReferenceEquals( content, this ) || WouldCreateParentCycle( content ) )
				return;

			if ( mountedInstanceId == Instance.State.InstanceId && ReferenceEquals( content.Parent, this ) )
				return;

			if ( !ReferenceEquals( content.Parent, this ) )
				content.Parent = this;

			mountedInstanceId = Instance.State.InstanceId;
		}
		catch ( NullReferenceException )
		{
			return;
		}
		catch ( Exception ex ) when ( ex.Message.Contains( "Removed Child but didn't have child!", StringComparison.OrdinalIgnoreCase ) )
		{
			return;
		}
		catch ( Exception ex )
		{
			Log.Warning( $"PaneOS app host mount skipped: {ex.Message}" );
		}
	}

	public override void OnDeleted()
	{
		try
		{
			if ( Instance?.Session.Content is { } content && ReferenceEquals( content.Parent, this ) )
				content.Parent = null;

			if ( mirrorSession?.Content is { } mirrorContent && ReferenceEquals( mirrorContent.Parent, this ) )
				mirrorContent.Parent = null;
		}
		catch ( Exception )
		{
		}

		base.OnDeleted();
	}

	private bool WouldCreateParentCycle( Panel content )
	{
		for ( Panel? ancestor = this; ancestor is not null; ancestor = ancestor.Parent )
		{
			if ( ReferenceEquals( ancestor, content ) )
				return true;
		}

		return false;
	}

	private Panel ResolveMountedContent()
	{
		if ( Instance?.Session.Content is null )
			return new Panel();

		var content = Instance.Session.Content;
		if ( content.IsValid() && (content.Parent is null || ReferenceEquals( content.Parent, this )) )
		{
			mirrorSession = null;
			return content;
		}

		if ( mirrorSession is null || mountedInstanceId != Instance.State.InstanceId || !mirrorSession.Content.IsValid() )
			mirrorSession = Instance.Runtime.CreateMirrorSession( Instance );

		return mirrorSession.Content;
	}
}