Editor/SubTerrainTool.cs
// Copyright (c) 2026 SubZero Studios LLC. All rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
using Editor.TerrainEditor;

namespace Editor.SubTerrain;

/// <summary>
/// SubTerrain editor tool. Extends the built-in terrain workflow with larger brushes,
/// slope/path/set-height sculpt modes, and paint texture support.
/// </summary>
[EditorTool( "tools.subterrain" )]
[Title( "SubTerrain" )]
[Icon( "terrain" )]
[Alias( "subterrain" )]
[Group( "Scene" )]
public class SubTerrainTool : EditorTool
{
	public static SubTerrainTool ActiveInstance { get; private set; }

	public SubTerrainBrushSettings BrushSettings { get; private set; } = SubTerrainBrushStore.ForMode( nameof( SubRaiseLowerTool ) );
	public SubTerrainPaintSettings PaintSettings { get; private set; } = new();
	public bool PaintArmed { get; set; }
	string _brushMode = nameof( SubRaiseLowerTool );

	BrushPreviewSceneObject _previewObject;
	SubTerrainStatusWidget _statusWidget;
	Widget _opacityWidget;
	Widget _speedWidget;
	Widget _rotationWidget;
	Widget _brushSheetHost;
	SubTerrainBrushSizeWidget _sizeWidget;
	Widget _sidebar;
	Widget _textureSection;
	Widget _statusSection;
	SubTerrainBlendWidget _blendWidget;
	Label _intentLabel;

	public Terrain PeekTerrain()
	{
		if ( StatusTerrain.IsValid() )
			return StatusTerrain;

		return GetSelectedComponent<Terrain>();
	}

	public void SetBrushMode( string key )
	{
		if ( string.IsNullOrEmpty( key ) )
			key = nameof( SubRaiseLowerTool );

		var next = SubTerrainBrushStore.ForMode( key );
		bool same = _brushMode == key && BrushSettings == next;
		_brushMode = key;
		BrushSettings = next;

		if ( !same )
		{
			RebuildBrushSheet();
			UpdateIntent();
		}

		UpdateOpacityState();
	}

	public void RefreshPaintChrome()
	{
		if ( _textureSection is not null )
			_textureSection.Visible = PaintArmed;

		if ( _blendWidget is not null )
			_blendWidget.Visible = PaintArmed;

		if ( _statusSection is not null )
			_statusSection.Visible = !PaintArmed;

		UpdateOpacityState();
		UpdateIntent();
	}

	public void UpdateIntent()
	{
		if ( _intentLabel is null )
			return;

		if ( PaintArmed )
		{
			_intentLabel.Text = "Paint Texture.";
			_intentLabel.ToolTip = "Alt sample | Ctrl fade | Shift smooth | ring yellow/green = Alt/Shift. Modes and size keys are in Shortcuts below.";
			return;
		}

		_intentLabel.Text = IntentForSculpt();
		_intentLabel.ToolTip = IntentTooltipForSculpt();
	}

	string IntentForSculpt()
	{
		return _brushMode switch
		{
			nameof( SubSmoothTool ) => "Smooth height.",
			nameof( SubPlainerTool ) => "Plainer: broad weighted leveling.",
			nameof( SubFlattenTool ) => "Flatten to click Z.",
			nameof( SubSlopeTool ) => "Slope: click start, drag to end.",
			nameof( SubPathTool ) => "Path: constant elevation strip.",
			nameof( SubSetHeightTool ) => "Flatten to Target Height.",
			nameof( SubNoiseTool ) => "Noise bump. Ctrl inverts.",
			nameof( SubHoleTool ) => "Punch hole. Ctrl fills.",
			nameof( SubBridgeTool ) => "Bridge: press start, release on end.",
			_ => "Raise height. Ctrl lowers."
		};
	}

	string IntentTooltipForSculpt()
	{
		var tip = _brushMode switch
		{
			nameof( SubPathTool ) => "Height-only strip. Control-map Path is Paint Texture (Ctrl+3).",
			nameof( SubSetHeightTool ) => "Alt-click loads Z into Target Height.",
			nameof( SubPlainerTool ) => "Like Hammer displacement smoothing across a wide brush.",
			nameof( SubBridgeTool ) => "Deck sits on the line between the two picked heights. Shortcut B.",
			_ => null
		};

		var keys = "Left / Right or Ctrl+[ / ] brush size. N activates SubTerrain. Keys 1-9, 0 Plainer, B Bridge.";
		return tip is null ? keys : $"{tip} {keys}";
	}

	/// <summary>
	/// Scene view destroys sidebar children on every subtool switch. Unparent first
	/// from subtool OnDisabled so we can reuse one panel and avoid width/content flash.
	/// </summary>
	public void DetachSidebarForReuse()
	{
		if ( _sidebar is not null && _sidebar.IsValid() )
			_sidebar.Parent = null;
	}

	public void NudgeBrushSize( int direction )
	{
		BrushSettings.Size = SubTerrainBrushSize.Nudge( BrushSettings.Size, direction );
		SyncBrushSizeWidget();
	}

	/// <summary>
	/// Sculpt modes only. Paint Texture keeps Up / Down for tmat selection.
	/// </summary>
	public bool NudgeBrushOpacity( int direction )
	{
		if ( PaintArmed || direction == 0 )
			return false;

		const float step = 0.05f;
		BrushSettings.Opacity = Math.Clamp( BrushSettings.Opacity + direction * step, 0f, 1f );
		RebuildBrushSheet();
		return true;
	}

	public void SyncBrushSizeWidget()
	{
		_sizeWidget?.SyncFromSettings();
	}

	public void SyncBrushRotationRows()
	{
		if ( _rotationWidget is not null )
			_rotationWidget.Enabled = !BrushSettings.RandomRotation;
	}

	public bool TrySetPaintLayer( SubTerrainPaintLayer layer )
	{
		if ( !PaintArmed || _blendWidget is null )
			return false;

		if ( layer == SubTerrainPaintLayer.Base )
			_blendWidget.SelectGround();
		else if ( layer == SubTerrainPaintLayer.Overlay )
			_blendWidget.SelectWear();
		else
			_blendWidget.SelectPath();

		return true;
	}

	/// <summary>
	/// Cycle selected paint tmat. Path skips the locked ground row (that role is Alt-click).
	/// </summary>
	public bool NudgePaintMaterial( int delta )
	{
		if ( !PaintArmed || delta == 0 )
			return false;

		var terrain = PeekTerrain();
		var materials = terrain?.Storage?.Materials;
		int count = materials?.Count ?? 0;
		if ( count <= 0 )
			return false;

		int skip = -1;
		if ( PaintSettings.Layer == SubTerrainPaintLayer.Path )
			skip = SubTerrainSplatUtil.FindPathGround( terrain, PaintSettings );

		int current = PaintSettings.MaterialIndex;
		if ( current < 0 || current >= count )
			current = 0;

		for ( int step = 0; step < count; step++ )
		{
			current = (current + delta) % count;
			if ( current < 0 )
				current += count;

			if ( current == skip )
				continue;

			PaintSettings.MaterialIndex = current;
			SubTerrainPaintTool.TrySetSplatChannel( current );
			_blendWidget?.OnMaterialSelectionChanged();
			return true;
		}

		return false;
	}

	public bool StatusValid { get; private set; }
	public Terrain StatusTerrain { get; private set; }
	public Vector3 StatusHitLocal { get; private set; }

	public static BrushList BrushList => TerrainEditorTool.BrushList;

	public Texture SelectedBrushTexture => BrushList?.Selected?.Texture;

	public void UpdateStatus( Terrain terrain, Vector3 hitLocal )
	{
		StatusValid = true;
		StatusTerrain = terrain;
		StatusHitLocal = hitLocal;
	}

	public void ClearStatus()
	{
		StatusValid = false;
		StatusTerrain = default;
	}

	public override IEnumerable<EditorTool> GetSubtools()
	{
		yield return new SubRaiseLowerTool( this );
		yield return new SubSmoothTool( this );
		yield return new SubPlainerTool( this );
		yield return new SubFlattenTool( this );
		yield return new SubTerrainPaintTool( this );
		yield return new SubSlopeTool( this );
		yield return new SubPathTool( this );
		yield return new SubSetHeightTool( this );
		yield return new SubNoiseTool( this );
		yield return new SubHoleTool( this );
		yield return new SubBridgeTool( this );
	}

	public override void OnEnabled()
	{
		ActiveInstance = this;
		AllowGameObjectSelection = false;
		RebuildSidebarOnSelectionChange = false;

		var selectedTerrain = GetSelectedComponent<Terrain>();
		if ( !selectedTerrain.IsValid() )
		{
			Selection.Clear();
			var first = Scene.GetAllComponents<Terrain>().FirstOrDefault();
			if ( first.IsValid() ) Selection.Add( first.GameObject );
		}
	}

	public override void OnDisabled()
	{
		if ( ActiveInstance == this )
			ActiveInstance = null;

		if ( _sidebar is not null && _sidebar.IsValid() )
		{
			_sidebar.Parent = null;
			_sidebar.Destroy();
		}

		_sidebar = null;
		_blendWidget = null;
		_intentLabel = null;
		_brushSheetHost = null;
		_sizeWidget = null;
		_opacityWidget = null;
		_rotationWidget = null;
		_textureSection = null;
		_statusSection = null;
		_statusWidget = null;

		_previewObject?.Delete();
		_previewObject = null;
		ClearStatus();
	}

	public override Widget CreateShortcutsWidget() => new SubTerrainToolShortcutsWidget();

	[Shortcut( "tools.subterrain", "n", typeof( SceneViewWidget ) )]
	public static void ActivateTool()
	{
		EditorToolManager.SetTool( nameof( SubTerrainTool ) );
	}

	public void DrawBrushPreview( Transform transform, Terrain terrain = null )
	{
		var brush = BrushList?.Selected?.Texture;
		if ( brush is null )
			return;

		_previewObject ??= new BrushPreviewSceneObject( Gizmo.World );

		var color = Color.FromBytes( 150, 150, 250 );
		var mods = Application.KeyboardModifiers;
		if ( mods.HasFlag( KeyboardModifiers.Alt ) )
			color = Color.FromBytes( 220, 200, 80 );
		else if ( mods.HasFlag( KeyboardModifiers.Shift ) )
			color = Color.FromBytes( 120, 200, 140 );
		else if ( mods.HasFlag( KeyboardModifiers.Ctrl ) )
			color = color.AdjustHue( 90 );

		color.a = BrushSettings.Opacity;

		_previewObject.RenderLayer = SceneRenderLayer.OverlayWithDepth;
		_previewObject.Bounds = BBox.FromPositionAndSize( 0, float.MaxValue );
		_previewObject.Transform = transform;
		_previewObject.Radius = BrushSettings.Size;
		_previewObject.Texture = brush;
		_previewObject.Color = color;
		_previewObject.BrushRotation = BrushSettings.Rotation;

		terrain ??= PeekTerrain();
		if ( BrushSettings.ShowGridPreview && terrain?.Storage is not null )
		{
			var tx = terrain.WorldTransform;
			_previewObject.CellSize = terrain.Storage.TerrainSize / terrain.Storage.Resolution;
			_previewObject.TerrainOrigin = tx.Position;
			_previewObject.TerrainRight = tx.Rotation.Right;
			_previewObject.TerrainForward = tx.Rotation.Forward;
		}
		else
		{
			_previewObject.CellSize = 0f;
		}
	}

	public override Widget CreateToolSidebar()
	{
		// Sidebar reuse: detach in OnDisabled before the scene view Clears the host.
		if ( _sidebar is ToolSidebarWidget existing && existing.IsValid() )
		{
			existing.Parent = null;
			RebuildBrushSheet();
			RefreshPaintChrome();
			return existing;
		}

		var sidebar = new ToolSidebarWidget();
		_sidebar = sidebar;
		sidebar.AddTitle( "SubTerrain", "terrain" );
		sidebar.ToolTip = $"SubTerrain {SubTerrainPaintSettings.LibraryVersion}";

		_intentLabel = sidebar.Layout.Add( new Label( "" ) );
		_intentLabel.Color = Theme.TextControl;
		sidebar.Layout.AddSpacingCell( 6 );

		_textureSection = new Widget( sidebar );
		_textureSection.Layout = Layout.Column();
		_blendWidget = new SubTerrainBlendWidget( this, _textureSection );
		_textureSection.Layout.Add( _blendWidget );
		sidebar.Layout.Add( _textureSection );
		_textureSection.Visible = false;

		{
			var group = sidebar.AddGroup( "Brush Type" );
			group.Add( new SubTerrainBrushPreviewWidget( sidebar ) );
		}

		{
			var group = sidebar.AddGroup( "Brush Properties" );
			_brushSheetHost = new Widget( sidebar );
			_brushSheetHost.Layout = Layout.Column();
			group.Add( _brushSheetHost );
			RebuildBrushSheet();
		}

		UpdateOpacityState();

		if ( !GetSelectedComponent<Terrain>().IsValid() )
		{
			var group = sidebar.AddGroup( "No Terrain" );
			group.Add( new Label( "Select a GameObject with a Terrain component." ) );
		}

		{
			var cursorLayout = sidebar.AddGroup( "Cursor" );
			_statusWidget = new SubTerrainStatusWidget( this, sidebar );
			cursorLayout.Add( _statusWidget );
			_statusSection = FindSidebarGroupHost( _statusWidget );
		}

		sidebar.AddShortcuts(
			("Activate SubTerrain", "N"),
			("Sculpt / paint tools", "1 - 9"),
			("Plainer / Bridge", "0 / B"),
			("Paint: Ground / Wear / Path", "Ctrl+1 / 2 / 3"),
			("Paint tmat prev / next", "Up / Down"),
			("Opacity (sculpt)", "Up / Down"),
			("Brush size", "Left / Right | Ctrl+[ / ]"),
			("Size / opacity", "MMB+Shift drag"),
			("Brush rotation", "MMB+Ctrl drag"),
			("Sample / fade / smooth", "Alt / Ctrl / Shift")
		);

		sidebar.Layout.AddStretchCell();
		UpdateIntent();
		return sidebar;
	}

	static Widget FindSidebarGroupHost( Widget child )
	{
		for ( var p = child?.Parent; p is not null; p = p.Parent )
		{
			if ( p.GetType().Name == "SidebarGroupWidget" )
				return p;
		}

		return child?.Parent;
	}

	[EditorEvent.Frame]
	public void TickSidebarChrome()
	{
		if ( ActiveInstance != this )
			return;

		if ( !PaintArmed )
			UpdateIntent();
	}

	void RebuildBrushSheet()
	{
		if ( _brushSheetHost is null )
			return;

		_brushSheetHost.Layout.Clear( true );
		_sizeWidget = null;
		_opacityWidget = null;
		_speedWidget = null;
		_rotationWidget = null;

		_sizeWidget = new SubTerrainBrushSizeWidget( this, _brushSheetHost );
		_brushSheetHost.Layout.Add( _sizeWidget );

		var so = BrushSettings.GetSerialized();
		var sheet = new ControlSheet();
		_opacityWidget = sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.Opacity ) ) );
		if ( _brushMode == nameof( SubRaiseLowerTool ) )
			_speedWidget = sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.Speed ) ) );
		_rotationWidget = sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.Rotation ) ) );
		var rndRot = sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.RandomRotation ) ) );
		sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.ShowGridPreview ) ) );
		if ( _brushMode == nameof( SubSetHeightTool ) )
			sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.TargetHeight ) ) );
		if ( _brushMode == nameof( SubBridgeTool ) )
		{
			sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.BridgeWidth ) ) );
			sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.BridgeEdgeFalloff ) ) );
			sheet.AddRow( so.GetProperty( nameof( SubTerrainBrushSettings.BridgeRaiseOnly ) ) );
		}

		so.OnPropertyChanged += OnBrushSheetPropertyChanged;

		_brushSheetHost.Layout.Add( sheet );
		SyncBrushRotationRows();
		UpdateOpacityState();
	}

	void OnBrushSheetPropertyChanged( SerializedProperty prop )
	{
		if ( prop?.Name == nameof( SubTerrainBrushSettings.RandomRotation ) )
			SyncBrushRotationRows();
	}

	void UpdateOpacityState()
	{
		if ( _speedWidget is not null )
			_speedWidget.ToolTip = "How fast height accumulates. Hold still to keep raising. Higher = more stamps and more height per stamp.";

		if ( _opacityWidget is null )
			return;

		if ( !PaintArmed )
		{
			bool bridge = _brushMode == nameof( SubBridgeTool );
			_opacityWidget.Enabled = !bridge;
			_opacityWidget.ToolTip = bridge
				? "Bridge always builds at the exact start and end heights."
				: OpacityTipForSculpt();
			return;
		}

		var layer = PaintSettings.Layer;
		if ( layer == SubTerrainPaintLayer.Overlay )
		{
			_opacityWidget.Enabled = true;
			_opacityWidget.ToolTip = "How fast wear builds on this stroke. Ctrl reduces.";
			return;
		}

		_opacityWidget.Enabled = false;
		_opacityWidget.ToolTip = layer == SubTerrainPaintLayer.Path
			? "Path uses Center mix and Core for the apron / solid ribbon. Pack center optional. Soft circle or stroke."
			: "Ground paint lays a solid tmat. Mix is cleared.";
	}

	string OpacityTipForSculpt()
	{
		return _brushMode switch
		{
			nameof( SubRaiseLowerTool ) => "Brush soft strength. Ctrl lowers. Use Speed for how fast height builds.",
			nameof( SubHoleTool ) => "How fast the punch cuts. Ctrl fills the hole back.",
			nameof( SubNoiseTool ) => "How strong the bump is. Ctrl inverts.",
			nameof( SubFlattenTool ) => "How fast it levels to the click height.",
			nameof( SubSetHeightTool ) => "How fast it levels to Target Height.",
			nameof( SubSlopeTool ) => "How fast it grades to the ramp.",
			nameof( SubPathTool ) => "How fast it levels to the click height.",
			nameof( SubSmoothTool ) => "How strong the smooth is.",
			nameof( SubPlainerTool ) => "How strong the weighted leveling is.",
			_ => "Stroke strength."
		};
	}
}

[Title( "Raise / Lower" )]
[Description( "Paint height up. Hold Ctrl to lower." )]
[Icon( "height" )]
[Alias( "subterrain_raise_lower" )]
[Group( "1" )]
[Order( 0 )]
public class SubRaiseLowerTool : SubTerrainBaseBrushTool
{
	public SubRaiseLowerTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.RaiseLower;
		AllowBrushInvert = true;
	}
}

[Title( "Flatten" )]
[Description( "Level to the height under the click. Stays on that plane while you drag." )]
[Icon( "trending_flat" )]
[Alias( "subterrain_flatten" )]
[Group( "1" )]
[Order( 3 )]
public class SubFlattenTool : SubTerrainBaseBrushTool
{
	public SubFlattenTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.Flatten;
	}

	public override bool GetHitPosition( Terrain terrain, out Vector3 position )
	{
		if ( _dragging )
		{
			var tx = terrain.WorldTransform;
			var hit = StrokePlane.Trace( Gizmo.CurrentRay, true );
			position = tx.PointToLocal( hit.Value );
			return hit.HasValue;
		}

		return base.GetHitPosition( terrain, out position );
	}
}

[Title( "Smooth" )]
[Description( "Blend neighbouring heights." )]
[Icon( "rounded_corner" )]
[Alias( "subterrain_smooth" )]
[Group( "1" )]
[Order( 1 )]
public class SubSmoothTool : SubTerrainBaseBrushTool
{
	public SubSmoothTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.Smooth;
	}

	public override bool GetHitPosition( Terrain terrain, out Vector3 position )
	{
		if ( _dragging )
		{
			var tx = terrain.WorldTransform;
			var hit = StrokePlane.Trace( Gizmo.CurrentRay, true );
			position = tx.PointToLocal( hit.Value );
			return hit.HasValue;
		}

		return base.GetHitPosition( terrain, out position );
	}
}

[Title( "Plainer" )]
[Description( "Gradually level a broad weighted area, like Hammer displacement smoothing." )]
[Icon( "blur_on" )]
[Alias( "subterrain_plainer" )]
[Group( "1" )]
[Order( 2 )]
public class SubPlainerTool : SubTerrainBaseBrushTool
{
	public SubPlainerTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.Plainer;
	}

	public override bool GetHitPosition( Terrain terrain, out Vector3 position )
	{
		if ( _dragging )
		{
			var tx = terrain.WorldTransform;
			var hit = StrokePlane.Trace( Gizmo.CurrentRay, true );
			position = tx.PointToLocal( hit.Value );
			return hit.HasValue;
		}

		return base.GetHitPosition( terrain, out position );
	}
}

[Title( "Noise" )]
[Description( "Add bump. Hold Ctrl to invert." )]
[Icon( "grain" )]
[Alias( "subterrain_noise" )]
[Group( "1" )]
[Order( 8 )]
public class SubNoiseTool : SubTerrainBaseBrushTool
{
	public SubNoiseTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.Noise;
		AllowBrushInvert = true;
	}
}

[Title( "Hole" )]
[Description( "Punch through the surface. Hold Ctrl to fill the hole back." )]
[Icon( "trip_origin" )]
[Alias( "subterrain_hole" )]
[Group( "1" )]
[Order( 9 )]
public class SubHoleTool : SubTerrainBaseBrushTool
{
	public SubHoleTool( SubTerrainTool parent ) : base( parent )
	{
		Mode = SubTerrainSculptMode.Hole;
		AllowBrushInvert = true;
	}
}

internal class SubTerrainBrushPreviewWidget : Widget
{
	public SubTerrainBrushPreviewWidget( Widget parent ) : base( parent )
	{
		MinimumSize = new( 32, 32 );
		MaximumSize = new( 48, 48 );
		Cursor = CursorShape.Finger;
	}

	protected override void OnPaint()
	{
		base.OnPaint();

		Paint.Antialiasing = true;
		Paint.ClearPen();
		Paint.DrawRect( LocalRect );

		var pixmap = SubTerrainTool.BrushList?.Selected?.Pixmap;
		if ( pixmap != null )
		{
			Paint.Draw( LocalRect.Contain( pixmap.Size ), pixmap );
		}
	}

	protected override void OnMouseClick( MouseEvent e )
	{
		var popup = new PopupWidget( null );
		popup.Position = Application.CursorPosition;
		popup.Visible = true;
		popup.Layout = Layout.Column();
		popup.Layout.Margin = 10;
		popup.MaximumSize = new Vector2( 300, 200 );

		var list = new SubTerrainBrushListWidget();
		list.BrushSelected += () =>
		{
			popup.Close();
			Update();
		};
		popup.Layout.Add( list );
	}

	[EditorEvent.Frame]
	public void UpdatePreview()
	{
		Update();
	}
}

internal class SubTerrainBrushListWidget : Widget
{
	public Action BrushSelected { get; set; }

	public SubTerrainBrushListWidget() : base( null )
	{
		ListView list = new();
		list.SetItems( SubTerrainTool.BrushList.Brushes.Cast<object>() );
		list.ItemSize = new Vector2( 40, 40 );
		list.ItemAlign = Sandbox.UI.Align.SpaceBetween;
		list.OnPaintOverride += () => PaintListBackground( list );
		list.ItemPaint = PaintBrushItem;
		list.ItemSelected = item =>
		{
			if ( item is Brush brush )
			{
				SubTerrainTool.BrushList.Selected = brush;
				BrushSelected?.Invoke();
			}
		};
		list.SelectItem( SubTerrainTool.BrushList.Selected );

		Layout = Layout.Column();

		var label = new Label( "Brushes" );
		label.SetStyles( "font-weight: bold" );
		Layout.Add( label );
		Layout.AddSpacingCell( 8 );
		Layout.Add( list );
	}

	private void PaintBrushItem( VirtualWidget widget )
	{
		var brush = (Brush)widget.Object;

		Paint.Antialiasing = true;
		Paint.TextAntialiasing = true;

		if ( widget.Hovered || widget.Selected )
		{
			Paint.ClearPen();
			Paint.SetBrush( widget.Selected ? Theme.Primary : Color.White.WithAlpha( 0.1f ) );
			Paint.DrawRect( widget.Rect.Grow( 2 ), 3 );
		}

		Paint.Draw( widget.Rect, brush.Pixmap );
	}

	private bool PaintListBackground( Widget widget )
	{
		Paint.ClearPen();
		Paint.SetBrush( Theme.ControlBackground );
		Paint.DrawRect( widget.LocalRect );
		return false;
	}
}

internal class SubTerrainToolShortcutsWidget : Widget
{
	[Shortcut( "tools.subterrain.raise-lower", "1", typeof( SceneViewWidget ) )]
	public void ActivateRaiseLower() => EditorToolManager.SetSubTool( nameof( SubRaiseLowerTool ) );

	[Shortcut( "tools.subterrain.smooth", "2", typeof( SceneViewWidget ) )]
	public void ActivateSmooth() => EditorToolManager.SetSubTool( nameof( SubSmoothTool ) );

	[Shortcut( "tools.subterrain.flatten", "3", typeof( SceneViewWidget ) )]
	public void ActivateFlatten() => EditorToolManager.SetSubTool( nameof( SubFlattenTool ) );

	[Shortcut( "tools.subterrain.paint", "4", typeof( SceneViewWidget ) )]
	public void ActivatePaint() => EditorToolManager.SetSubTool( nameof( SubTerrainPaintTool ) );

	[Shortcut( "tools.subterrain.slope", "5", typeof( SceneViewWidget ) )]
	public void ActivateSlope() => EditorToolManager.SetSubTool( nameof( SubSlopeTool ) );

	[Shortcut( "tools.subterrain.path", "6", typeof( SceneViewWidget ) )]
	public void ActivatePath() => EditorToolManager.SetSubTool( nameof( SubPathTool ) );

	[Shortcut( "tools.subterrain.set-height", "7", typeof( SceneViewWidget ) )]
	public void ActivateSetHeight() => EditorToolManager.SetSubTool( nameof( SubSetHeightTool ) );

	[Shortcut( "tools.subterrain.noise", "8", typeof( SceneViewWidget ) )]
	public void ActivateNoise() => EditorToolManager.SetSubTool( nameof( SubNoiseTool ) );

	[Shortcut( "tools.subterrain.hole", "9", typeof( SceneViewWidget ) )]
	public void ActivateHole() => EditorToolManager.SetSubTool( nameof( SubHoleTool ) );

	[Shortcut( "tools.subterrain.plainer", "0", typeof( SceneViewWidget ) )]
	public void ActivatePlainer() => EditorToolManager.SetSubTool( nameof( SubPlainerTool ) );

	[Shortcut( "tools.subterrain.bridge", "B", typeof( SceneViewWidget ) )]
	public void ActivateBridge() => EditorToolManager.SetSubTool( nameof( SubBridgeTool ) );

	[Shortcut( "tools.subterrain.paint-ground", "CTRL+1", typeof( SceneViewWidget ) )]
	public void PaintGround() => SubTerrainTool.ActiveInstance?.TrySetPaintLayer( SubTerrainPaintLayer.Base );

	[Shortcut( "tools.subterrain.paint-wear", "CTRL+2", typeof( SceneViewWidget ) )]
	public void PaintWear() => SubTerrainTool.ActiveInstance?.TrySetPaintLayer( SubTerrainPaintLayer.Overlay );

	[Shortcut( "tools.subterrain.paint-path", "CTRL+3", typeof( SceneViewWidget ) )]
	public void PaintPath() => SubTerrainTool.ActiveInstance?.TrySetPaintLayer( SubTerrainPaintLayer.Path );

	[Shortcut( "tools.subterrain.brush-smaller", "CTRL+[", typeof( SceneViewWidget ) )]
	public void BrushSmaller() => SubTerrainTool.ActiveInstance?.NudgeBrushSize( -1 );

	[Shortcut( "tools.subterrain.brush-larger", "CTRL+]", typeof( SceneViewWidget ) )]
	public void BrushLarger() => SubTerrainTool.ActiveInstance?.NudgeBrushSize( 1 );

	[Shortcut( "tools.subterrain.brush-smaller-arrow", "LEFT", typeof( SceneViewWidget ) )]
	public void BrushSmallerArrow()
	{
		if ( SubTerrainTool.ActiveInstance is not null )
		{
			SubTerrainTool.ActiveInstance.NudgeBrushSize( -1 );
			return;
		}

		EditorShortcuts.PassShortcut = true;
	}

	[Shortcut( "tools.subterrain.brush-larger-arrow", "RIGHT", typeof( SceneViewWidget ) )]
	public void BrushLargerArrow()
	{
		if ( SubTerrainTool.ActiveInstance is not null )
		{
			SubTerrainTool.ActiveInstance.NudgeBrushSize( 1 );
			return;
		}

		EditorShortcuts.PassShortcut = true;
	}

	[Shortcut( "tools.subterrain.paint-tmat-prev", "UP", typeof( SceneViewWidget ) )]
	public void ArrowUp()
	{
		var tool = SubTerrainTool.ActiveInstance;
		if ( tool is null )
		{
			EditorShortcuts.PassShortcut = true;
			return;
		}

		if ( tool.PaintArmed )
		{
			if ( tool.NudgePaintMaterial( -1 ) )
				return;

			EditorShortcuts.PassShortcut = true;
			return;
		}

		tool.NudgeBrushOpacity( 1 );
	}

	[Shortcut( "tools.subterrain.paint-tmat-next", "DOWN", typeof( SceneViewWidget ) )]
	public void ArrowDown()
	{
		var tool = SubTerrainTool.ActiveInstance;
		if ( tool is null )
		{
			EditorShortcuts.PassShortcut = true;
			return;
		}

		if ( tool.PaintArmed )
		{
			if ( tool.NudgePaintMaterial( 1 ) )
				return;

			EditorShortcuts.PassShortcut = true;
			return;
		}

		tool.NudgeBrushOpacity( -1 );
	}
}