Editor/Designers/ArchCorniceDesigner.cs
using System;
using System.Linq;
using Editor;
using Sandbox;

namespace Sunless.Architecture;

// Save writes back to .cornice.json, so what's tuned here becomes the shared style every elevation naming it wears.
public sealed class ArchCorniceDesigner : ArchDesignerDialog {
	readonly ArchKit kit;
	readonly Action<ArchCorniceStyle> applied;
	readonly ArchCorniceStyle draft;

	public ArchCorniceDesigner( Widget parent, ArchKit kit, ArchCorniceStyle initial, Action<ArchCorniceStyle> applied )
		: base( parent, "Cornice Designer", "horizontal_split" ) {
		this.kit = kit;
		this.applied = applied;
		draft = (initial ?? new ArchCorniceStyle()).Copy();

		Assemble(
			kit,
			"Live generated block",
			"Drag the preview to orbit. A crowned parapet through the same courses the plan builds with — a cornice is "
				+ "judged at a corner, where the mitre closes and a pier breaks the line.",
			$"Cornice styles: Assets/{ArchCorniceCatalog.Directory}" );
	}

	protected override string SaveLabel => "Save Style";

	protected override void Populate() {
		var form = Form.Layout;

		form.Add( Heading( "Identity" ) );
		form.Add( Text( "Name", draft.Name, value => draft.Name = value ) );
		form.Add( Text( "Detail", draft.Detail, value => draft.Detail = value ) );

		var options = new ToolSidebarWidget();

		ArchCorniceUi.Build( options, kit, draft, Repopulate, Stage.Restage );

		form.Add( options );
	}

	protected override ArchStaged Staged() => ArchCorniceStage.Of( draft, kit );

	protected override string Diagnose() {
		var head = kit.WallHeight;
		var layers = ArchCorniceGen.Layers( draft, head );

		return string.Join( "\n",
			$"style: {draft.Name} ({draft.Detail})",
			$"rise: {draft.Rise:0.##} hung from {head:0.##}, widest projection {draft.Projection:0.##}",
			$"courses: {layers.Count} of {draft.Courses.Count} standing",
			string.Join( "\n", layers.Select( layer =>
				$"  {layer.Course.Kind} {layer.Bottom:0.##} to {layer.Top:0.##}, "
				+ $"{(layer.Course.Straddles ? "oversails" : "proud")} {layer.Projection:0.##}"
				+ $"{(string.IsNullOrWhiteSpace( layer.Course.Profile ) ? "" : $", section {layer.Course.Profile}")}"
				+ $"{(layer.Course.Kind is CorniceCourse.Dentils or CorniceCourse.Modillions ? $", pitch {Pitch( layer.Course ):0.##}" : "")}"
				+ $", {layer.Course.Field}" ) ),
			draft.Pier.Stands
				? $"pier: {draft.Pier.Width:0.##} wide rising {draft.Pier.Rise:0.##} proud {draft.Pier.Projection:0.##}, "
					+ $"cap {draft.Pier.Cap:0.##} over {draft.Pier.CapOversail:0.##}"
				: "pier: none" );
	}

	float Pitch( ArchCorniceCourse course ) {
		return (course.Block > 0.05f ? course.Block : kit.DentilWidth) + (course.Gap > 0.05f ? course.Gap : kit.DentilGap);
	}

	protected override void Apply() {
		applied?.Invoke( draft.Copy() );
		Close();
	}

	protected override void SaveTemplate() {
		if ( string.IsNullOrWhiteSpace( draft.Name ) || ArchCorniceStage.Hidden( draft ) ) {
			Log.Warning( "Architecture: the cornice style needs a name of its own before it can be saved." );

			return;
		}

		var saved = draft.Copy();

		if ( !ArchShelved.Save( ArchShelf.Cornices, saved ) ) {
			Log.Warning( $"Architecture: could not write the cornice style '{saved.Name}'." );

			return;
		}

		// Onto the shelf under its own name, or the style saves to disk and the picker keeps offering the old stack
		// until the kit is reloaded.
		kit.Cornices.RemoveAll( style => string.Equals( style.Name, saved.Name, StringComparison.OrdinalIgnoreCase ) );
		kit.Cornices.Add( saved );

		Log.Info( $"Architecture: saved the cornice style '{saved.Name}'." );

		applied?.Invoke( saved );
		Close();
	}
}