Editor/Wall/ArchWallModUi.cs
using System;
using Editor;
using Sandbox;

namespace Sunless.Architecture;

// Read by the Wall detail subtool and by Select, off the same part - a draft and a placed modifier
// carry identical controls because they are the same record.
public static class ArchWallModUi {
	// What a block of masonry standing on or cut out of an elevation is worth being made of.
	static readonly ArchSurface[] Fields =
	{
		ArchSurface.Pillar,
		ArchSurface.WallBase,
		ArchSurface.WallExterior,
		ArchSurface.Foundation,
		ArchSurface.Panel,
		ArchSurface.Trim,
		ArchSurface.Sign
	};

	public static void Kinds( ToolSidebarWidget panel, ArchWallMod chosen, Action<ArchWallMod> adopt ) {
		using var grid = ArchIconGrid.In( panel.AddGroup( "Detail" ) );

		foreach ( var value in Enum.GetValues<ArchWallMod>() ) {
			var kind = value;

			grid.Pick( kind.Label(), $"wallmod_{kind}".ToLowerInvariant(), kind.Glyph(), chosen == kind, () => adopt( kind ) );
		}
	}

	// On surface is a placement mode - it asks the drag which face it landed on - so it is only ever
	// offered on a draft. A part already standing on a wall has an answer.
	public static void Sides( ToolSidebarWidget panel, ArchWallSide chosen, bool drafting, Action<ArchWallSide> adopt ) {
		using var grid = ArchIconGrid.In( panel.AddGroup( "Face" ) );

		foreach ( var value in Enum.GetValues<ArchWallSide>() ) {
			var side = value;

			if ( side == ArchWallSide.OnSurface && !drafting ) {
				continue;
			}

			grid.Pick( side.Label(), $"wallside_{side}".ToLowerInvariant(), side.Glyph(), chosen == side, () => adopt( side ) );
		}
	}

	public static void Build( ToolSidebarWidget panel, ArchWallModPart modifier, Action refresh, Action changed ) {
		Kinds( panel, modifier.Kind, kind => {
			Adopt( modifier, kind );

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

		Sides( panel, modifier.Side, false, side => {
			modifier.Side = side;
			changed?.Invoke();
			refresh?.Invoke();
		} );

		Sizes( panel, modifier, changed );
		Fill( panel, modifier, changed );
	}

	// Taking a kind is more than writing the field: the band and the reach come with it. Both the sheet and the
	// bool tool's detail page go through here, or picking Awning in one of them gives a canopy and in the other
	// a three-inch shelf.
	public static void Adopt( ArchWallModPart modifier, ArchWallMod kind ) {
		Rebanded( modifier, kind );

		modifier.Kind = kind;
		modifier.Field = kind.Field();
	}

	// A plate carries a band of its own where a pier climbs to the wall head, so a kind switch switches that
	// too: a plate's own two feet left on a pier is a stub where a whole elevation was asked for.
	static void Rebanded( ArchWallModPart modifier, ArchWallMod kind ) {
		// Through the same helper the resolve reads, or the sheet says three inches while the canopy builds
		// thirty-four.
		modifier.Depth = kind.Reach( modifier.Depth );

		if ( kind.Plated() ) {
			modifier.Height = kind.Rise( modifier.Height );

			return;
		}

		if ( modifier.Kind.Plated() ) {
			modifier.Base = 0f;
			modifier.Height = 0f;
		}
	}

	// Station first: it is the contract that keeps the modifier on the wall when the wall moves. A
	// draft has none to show - the drag is what names it.
	public static void Sizes( ToolSidebarWidget panel, ArchWallModPart modifier, Action changed, bool station = true ) {
		var group = panel.AddGroup( "Block" );

		if ( station ) {
			group.Add( ArchPartUi.Number( "Along the wall", modifier.Along, 0f, value => modifier.Along = value, changed ) );
		}

		group.Add( ArchPartUi.Number( station ? "Width" : "Width when clicked", modifier.Width, 16f, value => modifier.Width = value, changed ) );
		group.Add( ArchPartUi.Number( "Starts above the foot", modifier.Base, 0f, value => modifier.Base = value, changed ) );

		// A draft's height belongs to its own Climb group, which says it in storeys.
		if ( station ) {
			group.Add( ArchPartUi.Number( "Height", modifier.Height, 0f, value => modifier.Height = value, changed ) );
		}

		group.Add( ArchPartUi.Number(
			modifier.Carves ? "Bites in" : "Stands proud", modifier.Depth, 3f, value => modifier.Depth = value, changed ) );
	}

	public static void Fill( ToolSidebarWidget panel, ArchWallModPart modifier, Action changed ) {
		using var grid = ArchIconGrid.In( panel.AddGroup( "Material role" ) );

		foreach ( var value in Fields ) {
			var field = value;

			grid.Pick( $"Skinned as {field}", $"surface_{field}".ToLowerInvariant(), Glyph( field ),
				modifier.Field == field, () => { modifier.Field = field; changed?.Invoke(); } );
		}
	}

	static string Glyph( ArchSurface field ) => field switch {
		ArchSurface.Foundation => "foundation",
		ArchSurface.WallBase => "grid_4x4",
		ArchSurface.Panel => "crop_square",
		ArchSurface.Pillar => "view_column",
		ArchSurface.Trim => "horizontal_rule",
		ArchSurface.Sign => "label",
		_ => "villa"
	};
}