Editor/Wall/ArchOpening.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

public enum OpeningKind {
	Archway,
	Door,
	DoubleDoor,
	Garage,
	Window,
	Hatch
}

public enum GlassState {
	Intact,
	Broken
}

// What is fixed over the hole. The burglar bar leads because it is on every window of every house in these
// suburbs; a boarded panel, a roller shutter and a security gate are the same welded grid at another pitch,
// running the other way, at another depth.
public enum OpeningFurniture {
	None,
	Bars,
	Boarded,
	Shutter,
	Gate,
	// A pair hinged either side of the hole rather than a grid across it - the same bordered field of members,
	// resolved twice against the wall beside the reveal.
	Leaves
}

// What is built over the head, standing on the wall rather than in the hole. A hood is a shelf on corbels and a
// pediment is that shelf under a gable; neither is a kind of opening, because the hole under it is unchanged.
public enum OpeningHood {
	None,
	Cornice,
	Pediment
}

// WHERE a hole goes in a wall, in the wall's own along-and-up terms, so a module that wants an entrance can ask
// for one without resolving the unit that fills it.
public readonly struct ArchOpeningShape {
	public float Offset { get; init; }
	public float Width { get; init; }
	public float Height { get; init; }
	public float SillHeight { get; init; }
}

public sealed class ArchOpening : IArchPainted {
	public Dictionary<string, string> Extensions { get; set; } = new();
	public int Id { get; set; }
	// Deleting the part that punched it takes the hole - a stair walking through a wall owns its archway.
	public int OwnerId { get; set; }
	// The cut standing where this unit is. Re-derived every commit like any other effect, so dragging the
	// shaft off the window - or unticking Windows on it - hands the window straight back.
	public int SwallowedBy { get; set; }
	public string Preset { get; set; } = "door";
	public float Offset { get; set; }
	public float Width { get; set; }
	public float Height { get; set; }
	public float SillHeight { get; set; }
	public OpeningKind Kind { get; set; } = OpeningKind.Door;
	public bool Cased { get; set; } = true;
	public bool HasSill { get; set; } = true;
	public float CasingWidth { get; set; }
	public bool Leaf { get; set; } = true;
	public bool FlipHinge { get; set; }
	// Rides the engine's StartOpen - a leaf built open would slide twice as far when used.
	public bool StartOpen { get; set; }
	public bool Glazed { get; set; }
	public bool AutoLayout { get; set; } = true;
	// Units side by side in one opening, each with its own sashes.
	public int Lights { get; set; } = 1;
	public int Sashes { get; set; } = 2;
	// Per sash, not per unit - so 6-over-6 is three across by two down.
	public int PanesAcross { get; set; } = 2;
	public int PanesDown { get; set; } = 3;
	public GlassState Glass { get; set; } = GlassState.Intact;
	// Panes on their own object so fragmenting glass can find them - intent only for now.
	public bool Breakable { get; set; }
	public OpeningFurniture Furniture { get; set; }
	public OpeningHood Hood { get; set; }
	public bool BasementWell { get; set; }
	public float BasementWellDepth { get; set; } = 36f;
	// Art the author drew, standing where the extruded leaf would have. Aimed in the prefab's own space, so the
	// pivot Blender left behind never decides where it lands.
	public string LeafPrefab { get; set; } = "";
	public Vector3 LeafAnchor { get; set; }
	public Vector3 LeafAnchorAlong { get; set; }
	public Angles LeafAnchorTurn { get; set; }
	public bool LeafFits { get; set; }
	public string LeafMaterialGroup { get; set; } = "";
	public ArchPalette Palette { get; set; } = new();

	public float Left => Offset - Width * 0.5f;
	public float Right => Offset + Width * 0.5f;
	public float Top => SillHeight + Height;

	// The kind has the last word - a retyped preset keeps numbers that mean nothing.
	public bool IsGlazed => Glazed && Kind.Glazes();
	public bool IsSilled => HasSill && Kind.Sills();
	public bool IsHung => Leaf && Kind.Hangs();
	public OpeningFurniture Fitted => Kind.Carries( Furniture ) ? Furniture : OpeningFurniture.None;
	public OpeningHood Hooded => Kind.Wears( Hood ) ? Hood : OpeningHood.None;
	public ArchAimedPrefab LeafArt => new( LeafPrefab, LeafAnchor, LeafAnchorAlong, LeafAnchorTurn, LeafFits );
}