Editor/Tool/ArchLayerPieceRows.cs
using Editor;
using Sandbox;

namespace Sunless.Architecture;

sealed class ArchPieceRow : TreeNode {
	readonly ArchBuiltPiece piece;

	public ArchPieceRow( ArchBuiltPiece piece ) : base( piece.Key ) {
		this.piece = piece;
		Height = 22;

		foreach ( var child in piece.Children ) {
			AddItem( new ArchPieceRow( child ) );
		}
	}

	public ArchBuiltPiece Built => piece;

	public override void OnPaint( VirtualWidget item ) {
		var tool = ArchTool.Active;
		var rect = item.Rect;

		PaintSelection( item );

		var hidden = tool?.PieceHidden( piece.Owner, piece.Piece ) == true;
		var alpha = hidden ? 0.28f : 0.85f;

		Paint.SetPen( Theme.Text.WithAlpha( alpha ) );
		Paint.DrawIcon( rect.Shrink( 4, 0, 0, 0 ), ArchPieces.Glyph( piece.Name ), 14, TextFlag.LeftCenter );

		var body = rect.Shrink( 24, 0, ArchLayerStack.ControlWidth * 4f + 4f, 0 );

		Paint.DrawText( body, piece.Name, TextFlag.LeftCenter );

		Paint.SetPen( Theme.Text.WithAlpha( 0.3f * alpha ) );
		Paint.DrawText( body, piece.Renders ? "piece" : "group", TextFlag.RightCenter );

		Collision( rect );
		Shadow( rect );

		Paint.SetPen( Theme.Text.WithAlpha( hidden ? 0.75f : 0.3f ) );
		Paint.DrawIcon( Control( rect, 1 ), hidden ? "visibility_off" : "visibility", 14, TextFlag.Center );
	}

	void Collision( Rect rect ) {
		if ( ArchTool.Active is not { } tool ) {
			return;
		}

		var mode = ArchParts.Showing( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece, out var own );

		Paint.SetPen( !own
			? Theme.Text.WithAlpha( 0.3f )
			: mode == ArchCollisionMode.Complex ? Theme.Yellow : Theme.Blue );

		Paint.DrawIcon( Control( rect, 2 ), ArchCollision.Glyph( mode ), 14, TextFlag.Center );
	}

	void Shadow( Rect rect ) {
		if ( ArchTool.Active is not { } tool ) {
			return;
		}

		var casts = ArchParts.ShowingShadows( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece, out var own );

		Paint.SetPen( !own
			? Theme.Text.WithAlpha( 0.3f )
			: casts ? Theme.Blue : Theme.Yellow );

		Paint.DrawIcon( Control( rect, 3 ), ArchShadows.Glyph( casts ), 14, TextFlag.Center );
	}

	static Rect Control( Rect row, int fromRight ) {
		var width = ArchLayerStack.ControlWidth;

		return new Rect( row.Right - width * (fromRight + 1), row.Top, width, row.Height );
	}

	public override string GetTooltip() {
		return $"{piece.Piece} · built by layer {piece.Owner}"
			+ (piece.Renders ? "" : "\nA group over the pieces under it - it renders nothing itself.");
	}

	public override void OnActivated() {
		if ( ArchTool.Active is { } tool && piece.Object.IsValid() ) {
			tool.Frame( piece.Object.GetBounds() );
			tool.SelectLayer( tool.LayerTree.Find( piece.Owner ) );
		}
	}

	public void Collisions( ContextMenu menu, ArchTool tool ) {
		var showing = ArchParts.Showing( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece, out _ );
		var collisions = menu.AddMenu( "Collisions", ArchCollision.Glyph( showing ) );

		collisions.AddOption( ArchParts.Following( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece ),
			"settings", () => Collide( tool, null ) );

		collisions.AddSeparator();

		foreach ( var mode in new[] { ArchCollisionMode.Solids, ArchCollisionMode.Convex, ArchCollisionMode.Complex, ArchCollisionMode.None } ) {
			var captured = mode;

			collisions.AddOption( ArchCollision.Describe( captured ), ArchCollision.Glyph( captured ),
				() => Collide( tool, captured ) );
		}
	}

	public void Shadows( ContextMenu menu, ArchTool tool ) {
		var showing = ArchParts.ShowingShadows( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece, out _ );
		var shadows = menu.AddMenu( "Shadows", ArchShadows.Glyph( showing ) );

		shadows.AddOption( ArchParts.FollowingShadows( tool.Plan, tool.Kit, tool.LayerTree, piece.Owner, piece.Piece ),
			"settings", () => Cast( tool, null ) );

		shadows.AddSeparator();

		foreach ( var casts in new[] { true, false } ) {
			var captured = casts;

			shadows.AddOption( ArchShadows.Describe( captured ), ArchShadows.Glyph( captured ),
				() => Cast( tool, captured ) );
		}
	}

	void Cast( ArchTool tool, bool? casts ) {
		ArchParts.Record( tool.Plan, piece.Owner, piece.Piece ).Shadows = casts;

		tool.Plan.Parts.RemoveAll( record => record is { Collision: null, Shadows: null } );

		tool.Affect( ArchLayerGroups.Holding( tool.Plan, piece.Owner )?.Id ?? piece.Owner );
		tool.Commit( $"Shadows {piece.Name}" );
	}

	void Collide( ArchTool tool, ArchCollisionMode? mode ) {
		if ( mode is null ) {
			tool.Plan.Parts.RemoveAll( record => record.ItemId == piece.Owner && record.Piece == piece.Piece );
		} else {
			ArchParts.Record( tool.Plan, piece.Owner, piece.Piece ).Collision = mode;
		}

		tool.Affect( ArchLayerGroups.Holding( tool.Plan, piece.Owner )?.Id ?? piece.Owner );
		tool.Commit( $"Collisions {piece.Name}" );
	}

	public override bool OnContextMenu() {
		if ( ArchTool.Active is not { } tool ) {
			return false;
		}

		var menu = new ContextMenu( TreeView );

		menu.AddOption( tool.PieceHidden( piece.Owner, piece.Piece ) ? "Show" : "Hide",
			tool.PieceHidden( piece.Owner, piece.Piece ) ? "visibility" : "visibility_off",
			() => tool.TogglePieceHidden( piece.Owner, piece.Piece ) );

		Collisions( menu, tool );
		Shadows( menu, tool );

		menu.AddSeparator();
		menu.AddOption( "Frame", "filter_center_focus", OnActivated );

		menu.OpenAtCursor();

		return true;
	}
}