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

using System;
using Sandbox;

namespace Editor.SubTerrain;

/// <summary>
/// Brush Size row: piecewise track (fine under 3500, mid to 18000, coarse to 35000) plus type-in.
/// </summary>
internal sealed class SubTerrainBrushSizeWidget : Widget
{
	readonly SubTerrainTool _tool;
	readonly SubTerrainBrushSizeTrack _track;
	readonly LineEdit _field;
	bool _syncing;

	public SubTerrainBrushSizeWidget( SubTerrainTool tool, Widget parent ) : base( parent )
	{
		_tool = tool;

		Layout = Layout.Row();
		Layout.Spacing = 6;
		Layout.Margin = new Sandbox.UI.Margin( 0, 2, 10, 2 );

		var label = Layout.Add( new Label( "Size" ) );
		label.MinimumWidth = 72;
		label.Color = Theme.TextControl;
		label.ToolTip = "World units. Fine under 3500 (step 50), mid to 18000 (step 500), then to 35000 (step 1000). Left / Right or Ctrl+[ / ] steps.";

		_track = new SubTerrainBrushSizeTrack( this );
		Layout.Add( _track, 1 );

		_field = new LineEdit();
		_field.MinimumWidth = 52;
		_field.MaximumWidth = 56;
		_field.ToolTip = "Type an exact size (8-35000).";
		_field.EditingFinished += OnFieldFinished;
		Layout.Add( _field );

		FixedHeight = 28;
		MinimumHeight = 28;
		MaximumHeight = 28;

		SyncFromSettings();
	}

	public void SyncFromSettings()
	{
		if ( _tool?.BrushSettings is null )
			return;

		_syncing = true;
		int size = SubTerrainBrushSize.Clamp( _tool.BrushSettings.Size );
		_tool.BrushSettings.Size = size;
		_field.Text = size.ToString();
		_track.Update();
		_syncing = false;
	}

	public void SetSizeFromTrack( float t01 )
	{
		if ( _syncing || _tool?.BrushSettings is null )
			return;

		int size = SubTerrainBrushSize.SizeFromTrack( t01 );
		if ( size == _tool.BrushSettings.Size )
		{
			_track.Update();
			return;
		}

		_tool.BrushSettings.Size = size;
		_syncing = true;
		_field.Text = size.ToString();
		_syncing = false;
		_track.Update();
	}

	void OnFieldFinished()
	{
		if ( _syncing || _tool?.BrushSettings is null )
			return;

		if ( !int.TryParse( _field.Text.Trim(), out int parsed ) )
		{
			SyncFromSettings();
			return;
		}

		_tool.BrushSettings.Size = SubTerrainBrushSize.Clamp( parsed );
		SyncFromSettings();
	}
}

internal sealed class SubTerrainBrushSizeTrack : Widget
{
	readonly SubTerrainBrushSizeWidget _host;
	bool _dragging;

	public SubTerrainBrushSizeTrack( SubTerrainBrushSizeWidget host ) : base( host )
	{
		_host = host;
		MouseTracking = true;
		Cursor = CursorShape.Finger;
		MinimumHeight = 22;
		MaximumHeight = 22;
		ToolTip = "Drag: fine under 3500, mid to 18000, coarse to 35000. Left / Right or Ctrl+[ / ] use the same steps.";
	}

	protected override void OnMousePress( MouseEvent e )
	{
		if ( e.Button == MouseButtons.Left )
		{
			_dragging = true;
			ApplyAt( e.LocalPosition.x );
			e.Accepted = true;
			return;
		}

		base.OnMousePress( e );
	}

	protected override void OnMouseMove( MouseEvent e )
	{
		base.OnMouseMove( e );
		if ( _dragging )
			ApplyAt( e.LocalPosition.x );
	}

	protected override void OnMouseReleased( MouseEvent e )
	{
		if ( e.Button == MouseButtons.Left )
			_dragging = false;

		base.OnMouseReleased( e );
	}

	void ApplyAt( float x )
	{
		float w = Math.Max( Width - 8f, 1f );
		float t = Math.Clamp( (x - 4f) / w, 0f, 1f );
		_host.SetSizeFromTrack( t );
	}

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

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

		var track = new Rect( 4, Height * 0.5f - 2f, Math.Max( Width - 8f, 1f ), 4f );
		Paint.SetBrush( Color.White.WithAlpha( 0.08f ) );
		Paint.DrawRect( track, 2 );

		// Piecewise band ticks (fine / mid).
		float fineX = track.Left + track.Width * SubTerrainBrushSize.FineTrack;
		float midX = track.Left + track.Width * SubTerrainBrushSize.MidTrack;
		Paint.SetBrush( Color.White.WithAlpha( 0.14f ) );
		Paint.DrawRect( new Rect( fineX, track.Top - 2f, 1f, track.Height + 4f ) );
		Paint.DrawRect( new Rect( midX, track.Top - 2f, 1f, track.Height + 4f ) );

		var tool = SubTerrainTool.ActiveInstance;
		int size = tool?.BrushSettings is not null
			? SubTerrainBrushSize.Clamp( tool.BrushSettings.Size )
			: SubTerrainBrushSize.Min;

		float t = SubTerrainBrushSize.TrackFromSize( size );
		float thumbX = track.Left + track.Width * t;
		float thumbR = 6f;
		Paint.SetBrush( Theme.Primary );
		Paint.DrawCircle( new Vector2( thumbX, Height * 0.5f ), thumbR );
	}
}