Editor/UI/ArchCorniceUi.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Editor;

namespace Sunless.Architecture;

// The picker's card, and the course-by-course form behind the Design button. The sheet gets ONE combo; every
// dimension a cornice has lives in here, reached deliberately, because the stack was proportioned together and a
// row of numbers on the elevation is six ways to pull it apart.
public static class ArchCorniceUi {
	// What a course is worth being made of. Not every ArchSurface - most are not trim at all.
	static readonly ArchSurface[] Fields =
	{
		ArchSurface.Trim,
		ArchSurface.WallCap,
		ArchSurface.WallExterior,
		ArchSurface.Pillar,
		ArchSurface.WallBase,
		ArchSurface.Panel
	};

	public static IEnumerable<ArchCorniceStyle> Standing( ArchKit kit ) {
		return kit.Cornices.Where( style => !ArchCorniceStage.Hidden( style ) );
	}

	public static ArchPresetItem<ArchCorniceStyle> Card( ArchCorniceStyle style, ArchKit kit ) {
		return new ArchPresetItem<ArchCorniceStyle> {
			Value = style,
			Name = style.Name,
			Detail = ArchCorniceStage.Describe( style ),
			Identity = ArchCorniceStage.Identity( style ),
			Category = style.Pier.Stands ? "Piered" : "Plain",
			Glyph = "horizontal_split",
			Tags = string.Join( " ", style.Courses.Select( course => course.Kind.ToString() ).Distinct() ),
			View = ArchPresetPreview.Raised,
			Interest = ArchCorniceStage.Interest( style, kit ),
			Recipe = () => ArchCorniceStage.Of( style, kit )
		};
	}

	// THE cornice chooser, wherever a head is crowned - a roof edge, a wall head, a parapet. Not having one is a
	// card in the same grid rather than a toggle in some other group: the entablature is the loudest thing an
	// elevation gets out of this tool, and it spent its life behind one unlabelled icon nobody found.
	public static Widget Picker( Widget parent, ArchKit kit, string stateKey, Func<string> current, Action<string> chosen ) {
		var browser = new ArchPresetBrowser<ArchCorniceStyle>( parent, kit, stateKey, "Cornice" ) {
			Chosen = style => style is null
				? kit.FindCornice( current() ) is not { Stands: true }
				: string.Equals( current(), style.Name, StringComparison.OrdinalIgnoreCase ),
			Choose = style => chosen( style?.Name ?? "" ),
			Design = () => new ArchCorniceDesigner( parent, kit, kit.FindCornice( current() ), designed => chosen( designed.Name ) ).Show()
		};

		var cards = new List<ArchPresetItem<ArchCorniceStyle>> { Bare() };

		cards.AddRange( Standing( kit ).Select( style => Card( style, kit ) ) );

		browser.Set( cards );
		browser.DesignButton.Visible = true;
		browser.DesignButton.ToolTip = "Design a cornice style";

		return browser;
	}

	static ArchPresetItem<ArchCorniceStyle> Bare() {
		return new ArchPresetItem<ArchCorniceStyle> {
			Value = null,
			Name = "None",
			Detail = "A bare head",
			Identity = "cornice.none",
			Category = "Plain",
			Glyph = "block"
		};
	}

	// The stack an author most likely means: the plainest one that is still a cornice rather than a bare cap.
	public static string Offered( ArchKit kit ) {
		var wanted = Standing( kit ).FirstOrDefault( style => style.Courses.Any( course => course.Kind == CorniceCourse.Moulding ) )
			?? Standing( kit ).FirstOrDefault();

		return wanted?.Name ?? "";
	}

	public static void Build( ToolSidebarWidget panel, ArchKit kit, ArchCorniceStyle style, Action refresh, Action changed ) {
		Courses( panel, kit, style, refresh, changed );
		Piers( panel, style, refresh, changed );
	}

	// Stated bottom up, the way it is built and the way a section drawing reads. Order is the list's own, so moving
	// a course is moving a row rather than re-typing two heights.
	static void Courses( ToolSidebarWidget panel, ArchKit kit, ArchCorniceStyle style, Action refresh, Action changed ) {
		var group = panel.AddGroup( $"Courses ({style.Courses.Count}, bottom up)" );

		for ( var index = 0; index < style.Courses.Count; index++ ) {
			Course( panel, group, kit, style, index, refresh, changed );
		}

		using var add = ArchIconGrid.In( group );

		add.Toggle( "Add a course on top of the stack", "cornice_add", "add", false, _ => {
			style.Courses.Add( new ArchCorniceCourse() );
			changed?.Invoke();
			refresh?.Invoke();
		} );
	}

	static void Course( Widget panel, Layout group, ArchKit kit, ArchCorniceStyle style, int index, Action refresh, Action changed ) {
		var course = style.Courses[index];
		var seat = index;

		using ( var kinds = ArchIconGrid.In( group ) ) {
			foreach ( var value in Enum.GetValues<CorniceCourse>() ) {
				var wanted = value;

				kinds.Pick( Describe( wanted ), $"cornice_{wanted}".ToLowerInvariant(), Glyph( wanted ),
					course.Kind == wanted, () => { course.Kind = wanted; changed?.Invoke(); refresh?.Invoke(); } );
			}
		}

		group.Add( ArchPartUi.Number( "Rise", course.Rise, 4f, value => course.Rise = value, changed ) );
		group.Add( ArchPartUi.Number( course.Straddles ? "Oversails by" : "Stands proud", course.Projection, 3f,
			value => course.Projection = value, changed ) );

		if ( course.Kind is CorniceCourse.Moulding or CorniceCourse.Modillions ) {
			group.Add( Sections( panel, kit, course, changed ) );
		}

		if ( course.Kind is CorniceCourse.Dentils or CorniceCourse.Modillions ) {
			// A pitch, never a count: the count comes off whatever run the course lands on.
			group.Add( ArchPartUi.Number( "Block width", course.Block, kit.DentilWidth, value => course.Block = value, changed ) );
			group.Add( ArchPartUi.Number( "Gap beside it", course.Gap, kit.DentilGap, value => course.Gap = value, changed ) );
		}

		using ( var fields = ArchIconGrid.In( group ) ) {
			foreach ( var role in Fields ) {
				var wanted = role;

				fields.Pick( $"Skinned as {wanted}", $"cornice_surface_{wanted}".ToLowerInvariant(), FieldGlyph( wanted ),
					course.Field == wanted, () => { course.Field = wanted; changed?.Invoke(); } );
			}
		}

		using var row = ArchIconGrid.In( group );

		row.Toggle( "Move this course down the stack", "cornice_lower", "arrow_downward", false,
			_ => Reorder( style, seat, -1, refresh, changed ) );

		row.Toggle( "Move this course up the stack", "cornice_raise", "arrow_upward", false,
			_ => Reorder( style, seat, 1, refresh, changed ) );

		row.Toggle( $"Remove the {course.Kind} course", "cornice_remove", "delete", false, _ => {
			style.Courses.RemoveAt( seat );
			changed?.Invoke();
			refresh?.Invoke();
		} );
	}

	static void Reorder( ArchCorniceStyle style, int index, int step, Action refresh, Action changed ) {
		var wanted = index + step;

		if ( wanted < 0 || wanted >= style.Courses.Count ) {
			return;
		}

		(style.Courses[index], style.Courses[wanted]) = (style.Courses[wanted], style.Courses[index]);

		changed?.Invoke();
		refresh?.Invoke();
	}

	static Widget Sections( Widget parent, ArchKit kit, ArchCorniceCourse course, Action changed ) {
		var browser = new ArchPresetBrowser<ArchProfile>( parent, kit, "cornice.sections", "Section" ) {
			Chosen = profile => string.Equals( course.Profile, profile.Name, StringComparison.OrdinalIgnoreCase ),
			Choose = profile => {
				course.Profile = profile.Name;
				changed?.Invoke();
			}
		};

		browser.Set( kit.Profiles
			.Select( profile => new ArchPresetItem<ArchProfile> {
				Value = profile,
				Name = profile.Name,
				Detail = $"{profile.Max.x - profile.Min.x:0.#} x {profile.Max.y - profile.Min.y:0.#} in",
				Identity = profile.Name,
				Category = "Section",
				Glyph = "show_chart"
			} )
			.ToList() );

		return browser;
	}

	static void Piers( ToolSidebarWidget panel, ArchCorniceStyle style, Action refresh, Action changed ) {
		var pier = style.Pier;

		using ( var grid = ArchIconGrid.In( panel.AddGroup( "Piers" ) ) ) {
			grid.Toggle( "Break the line above the cornice at every corner", "opt_cornice_piers", "view_column", pier.Stands,
				value => { pier.Rise = value ? 16f : 0f; changed?.Invoke(); refresh?.Invoke(); } );
		}

		if ( !pier.Stands ) {
			return;
		}

		var group = panel.AddGroup( "Pier" );

		group.Add( ArchPartUi.Number( "Rises above the head", pier.Rise, 16f, value => pier.Rise = value, changed ) );
		group.Add( ArchPartUi.Number( "Width", pier.Width, 16f, value => pier.Width = value, changed ) );
		group.Add( ArchPartUi.Number( "Stands proud", pier.Projection, 2f, value => pier.Projection = value, changed ) );
		group.Add( ArchPartUi.Number( "Cap height", pier.Cap, 3f, value => pier.Cap = value, changed ) );
		group.Add( ArchPartUi.Number( "Cap oversails by", pier.CapOversail, 1.5f, value => pier.CapOversail = value, changed ) );
	}

	public static string Describe( CorniceCourse kind ) => kind switch {
		CorniceCourse.Moulding => "Moulding — a swept section standing proud of the face",
		CorniceCourse.Band => "Band — a plain projecting course, the corona",
		CorniceCourse.Dentils => "Dentils — a row of small blocks",
		CorniceCourse.Modillions => "Modillions — the same row, cut from a section",
		_ => "Coping — a cap straddling the wall, oversailing both faces"
	};

	static string Glyph( CorniceCourse kind ) => kind switch {
		CorniceCourse.Moulding => "waves",
		CorniceCourse.Band => "horizontal_rule",
		CorniceCourse.Dentils => "view_week",
		CorniceCourse.Modillions => "view_column",
		_ => "border_top"
	};

	static string FieldGlyph( ArchSurface field ) => field switch {
		ArchSurface.WallCap => "border_top",
		ArchSurface.WallExterior => "villa",
		ArchSurface.Pillar => "view_column",
		ArchSurface.WallBase => "grid_4x4",
		ArchSurface.Panel => "crop_square",
		_ => "horizontal_rule"
	};
}