using System.Linq;
namespace OpenNest.Data;
///
/// Plate and drawing rows for one saved nest, shown below the nest list in the
/// Database-mode nest browser. The server stores only nest-level metadata, so these
/// rows are built from the nest's downloaded archive.
///
public sealed class NestDetails
{
public Units Units { get; init; }
/// One row per plate, in nest order.
public IReadOnlyList Plates { get; init; } = Array.Empty();
/// One row per drawing (cutoffs excluded), in nest order.
public IReadOnlyList Drawings { get; init; } = Array.Empty();
///
/// The plates themselves, in the same order as , for the plate
/// preview. They belong to the downloaded copy of the nest, never to an open document.
///
public IReadOnlyList PlateLayouts { get; init; } = Array.Empty();
public static NestDetails FromNest(Nest nest)
{
ArgumentNullException.ThrowIfNull(nest);
var plates = new List(nest.Plates.Count);
var nested = new Dictionary(ReferenceEqualityComparer.Instance);
for (var i = 0; i < nest.Plates.Count; i++)
{
var plate = nest.Plates[i];
var parts = plate.Parts.Where(part => !part.BaseDrawing.IsCutOff).ToList();
foreach (var part in parts)
nested[part.BaseDrawing] = nested.GetValueOrDefault(part.BaseDrawing) + plate.Quantity;
plates.Add(new NestPlateDetail(
Number: i + 1,
Duplicates: plate.Quantity,
Width: plate.Size.Width,
Length: plate.Size.Length,
PartCount: parts.Count,
DrawingCount: parts.Select(part => part.BaseDrawing).Distinct(ReferenceEqualityComparer.Instance).Count(),
// A zero-size plate has no meaningful utilization; report 0 rather than NaN.
Utilization: plate.Area() > 0 ? plate.Utilization() : 0));
}
var drawings = nest.Drawings
.Where(drawing => !drawing.IsCutOff)
.Select(drawing => new NestDrawingDetail(
Name: drawing.Name ?? "",
Customer: drawing.Customer ?? "",
Required: drawing.Quantity.Required,
Nested: nested.GetValueOrDefault(drawing),
Area: drawing.Area))
.ToList();
return new NestDetails
{
Units = nest.Units,
Plates = plates,
Drawings = drawings,
PlateLayouts = nest.Plates.ToList(),
};
}
}
/// 1-based position of the plate in the nest.
/// How many times the plate is cut ().
/// Parts on one copy of the plate, cutoffs excluded.
/// Fraction of the plate area used by parts, 0 to 1.
public sealed record NestPlateDetail(
int Number,
int Duplicates,
double Width,
double Length,
int PartCount,
int DrawingCount,
double Utilization);
/// Parts placed across all plates, counting each plate's duplicates.
public sealed record NestDrawingDetail(string Name, string Customer, int Required, int Nested, double Area)
{
public int Remaining => System.Math.Max(0, Required - Nested);
}