Editor/Pipe/ArchPipeFittings.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

public enum ArchPipeFittingKind {
	Flange,
	Coupling,
	Elbow,
	Tee,
	Reducer
}

public sealed class ArchPipeFitting {
	public int NodeId { get; init; }
	public Vector3 Position { get; init; }
	public ArchPipeFittingKind Kind { get; init; }
	public List<int> Runs { get; init; } = new();
}

public static class ArchPipeFittings {
	readonly record struct Leg( ArchPipePart Run, Vector3 Direction );

	public static List<ArchPipeFitting> Resolve( ArchPlan plan, ArchPipePart part ) {
		return part.Nodes
			.Where( node => node.Id != 0 )
			.Select( node => At( plan, node.Id ) )
			.Where( fitting => fitting is not null )
			.DistinctBy( fitting => fitting.NodeId )
			.ToList();
	}

	public static ArchPipeFitting At( ArchPlan plan, int nodeId ) {
		var legs = new List<Leg>();
		var position = Vector3.Zero;
		var found = false;

		foreach ( var run in ArchPipe.All( plan ) ) {
			for ( var index = 0; index < run.Nodes.Count; index++ ) {
				if ( run.Nodes[index].Id != nodeId ) {
					continue;
				}

				position = run.Nodes[index].Position;
				found = true;

				if ( index > 0 ) {
					legs.Add( new Leg( run, (run.Nodes[index - 1].Position - position).Normal ) );
				}

				if ( index < run.Nodes.Count - 1 ) {
					legs.Add( new Leg( run, (run.Nodes[index + 1].Position - position).Normal ) );
				}
			}
		}

		if ( !found || legs.Count == 0 ) {
			return null;
		}

		var runs = legs.Select( leg => leg.Run ).Distinct().ToList();
		var diameters = runs.Select( run => run.Diameter ).Distinct().Count();
		var kind = legs.Count switch {
			1 => ArchPipeFittingKind.Flange,
			>= 3 => ArchPipeFittingKind.Tee,
			_ when diameters > 1 => ArchPipeFittingKind.Reducer,
			_ when Vector3.Dot( legs[0].Direction, legs[1].Direction ) > -0.999f => ArchPipeFittingKind.Elbow,
			_ => ArchPipeFittingKind.Coupling
		};

		return new ArchPipeFitting {
			NodeId = nodeId,
			Position = position,
			Kind = kind,
			Runs = runs.Select( run => run.Id ).ToList()
		};
	}
}