Editor/Data/ArchCorniceCatalog.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sunless.Architecture;

// The entablatures a wall head or a parapet ring can wear. Shipped whole rather than assembled on the sheet: an
// author picks one name and gets a stack that was proportioned together, which is the only way a cornice reads.
public sealed class ArchCorniceCatalog : IArchCatalog {
	public static string Directory => ArchDataRoot.For( "cornices" );

	public ArchShelf Shelf => ArchShelf.Cornices;

	public string Named( object entry ) => entry is ArchCorniceStyle style ? style.Name : null;

	public IEnumerable<object> Shipped() => Styles();

	// Written out on first read, the way a pillar type is: a cornice is tuned far more often than it is invented,
	// so an author opens the folder and moves a course rather than typing a stack from nothing.
	public IEnumerable<object> Authored() {
		foreach ( var built in Styles() ) {
			var path = PathFor( built.Name );

			if ( !ArchStorage.DocumentExists( path ) ) {
				ArchStorage.WriteDocument( path, built );
			}
		}

		var loaded = new List<ArchCorniceStyle>();

		foreach ( var file in ArchStorage.DocumentFiles( Directory, ".cornice.json" ) ) {
			if ( ArchStorage.ReadDocument<ArchCorniceStyle>( $"{Directory}/{file}" ) is { Name.Length: > 0, Courses: not null } style ) {
				loaded.Add( style );
			}
		}

		return loaded.OrderBy( style => style.Name ).ToList();
	}

	public bool Save( object entry ) => entry is ArchCorniceStyle style && ArchStorage.WriteDocument( PathFor( style.Name ), style );

	static string PathFor( string name ) => $"{Directory}/{name}.cornice.json";

	public static List<ArchCorniceStyle> Styles() {
		return new List<ArchCorniceStyle>
		{
			new()
			{
				Name = "coping",
				Detail = "A single cap oversailing both faces",
				Courses = new List<ArchCorniceCourse> { Coping( 4f, 2f ) }
			},
			new()
			{
				Name = "stepped_coping",
				Detail = "Two caps, the upper held back",
				Courses = new List<ArchCorniceCourse> { Coping( 3.5f, 2.5f ), Coping( 2.5f, 1f ) }
			},
			new()
			{
				Name = "plain_cornice",
				Detail = "Bed moulding, corona and crown",
				Courses = new List<ArchCorniceCourse>
				{
					Moulding( "bed", 3.5f, 3f ),
					Band( 6f, 7f ),
					Moulding( "crown", 9f, 9f ),
					Coping( 4f, 2f )
				}
			},
			new()
			{
				Name = "dentilled",
				Detail = "A dentil course under the corona",
				Courses = new List<ArchCorniceCourse>
				{
					Moulding( "bed", 3.5f, 3f ),
					Blocks( CorniceCourse.Dentils, "", 5f, 6f, 2.6f, 2.6f ),
					Band( 5f, 7.5f ),
					Moulding( "crown", 9f, 9f ),
					Coping( 4f, 2f )
				}
			},
			new()
			{
				Name = "bracketed",
				Detail = "Modillion brackets carrying the corona",
				Courses = new List<ArchCorniceCourse>
				{
					Moulding( "bed", 3.5f, 3f ),
					Blocks( CorniceCourse.Modillions, "modillion", 5f, 7f, 3f, 9f ),
					Band( 5f, 8f ),
					Moulding( "crown", 9f, 9f ),
					Coping( 4f, 2f )
				}
			},
			new()
			{
				Name = "main_street",
				// The crown IS the roof edge: nothing stands over it, so there is no coping course and no pier
				// stepping above - a second cap on top reads as another wall standing on the entablature.
				Detail = "Dentilled, the crown finishing the wall",
				Courses = new List<ArchCorniceCourse>
				{
					Moulding( "bed", 3.5f, 3f ),
					Band( 5f, 5f ),
					Blocks( CorniceCourse.Dentils, "", 5f, 6.5f, 2.6f, 2.6f ),
					Moulding( "crown", 9f, 9f )
				},
				Bracket = new ArchCorniceBracket { Width = 6f, Drop = 0.1f, Projection = 8f }
			}
		};
	}

	static ArchCorniceCourse Moulding( string profile, float rise, float projection ) {
		return new ArchCorniceCourse {
			Kind = CorniceCourse.Moulding,
			Profile = profile,
			Rise = rise,
			Projection = projection
		};
	}

	static ArchCorniceCourse Band( float rise, float projection ) {
		return new ArchCorniceCourse { Kind = CorniceCourse.Band, Rise = rise, Projection = projection };
	}

	static ArchCorniceCourse Coping( float rise, float projection ) {
		return new ArchCorniceCourse {
			Kind = CorniceCourse.Coping,
			Rise = rise,
			Projection = projection,
			Field = ArchSurface.WallCap
		};
	}

	static ArchCorniceCourse Blocks( CorniceCourse kind, string profile, float rise, float projection, float block, float gap ) {
		return new ArchCorniceCourse {
			Kind = kind,
			Profile = profile,
			Rise = rise,
			Projection = projection,
			Block = block,
			Gap = gap
		};
	}
}