using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace OpenNest.IO.Bom;
/// Whether a BOM row can be imported, and if not, why.
public enum BomRowStatus
{
/// Drawing found and every value set: the row is imported.
Ready,
/// Drawing found but the material is blank.
NeedsMaterial,
/// Drawing found but the thickness is blank, zero or negative.
NeedsThickness,
/// Drawing found but the quantity is below 1.
NeedsQuantity,
/// No drawing file matches the row's file name.
NoDrawing,
/// The BOM row has no file name.
NoFileName,
}
///
/// One BOM line as the import dialog shows and edits it: the values read
/// from the BOM, the drawing file it resolved to and whether it can be
/// imported. Changing Material, Thickness or Qty raises
/// for that value and for the status.
///
public class BomPartRow : INotifyPropertyChanged
{
private string material;
private double? thickness;
private int? qty;
public event PropertyChangedEventHandler PropertyChanged;
public int? ItemNum { get; set; }
public string FileName { get; set; }
/// The quantity the BOM gave, unchanged by edits; null when blank.
public int? BomQty { get; set; }
///
/// The quantity to import. Set it only to a value accepted by
/// , or use .
/// Setting it clears .
///
public int? Qty
{
get => qty;
set
{
QtyAssumed = false;
Set(ref qty, value);
}
}
/// True while Qty is the 1 used for a blank BOM quantity.
public bool QtyAssumed { get; set; }
public string Description { get; set; }
public string Material
{
get => material;
set => Set(ref material, value);
}
public double? Thickness
{
get => thickness;
set => Set(ref thickness, value);
}
/// The matched drawing file, or null when none was found.
public string DxfPath { get; set; }
public BomRowStatus Status
{
get
{
if (string.IsNullOrWhiteSpace(FileName))
return BomRowStatus.NoFileName;
if (string.IsNullOrWhiteSpace(DxfPath))
return BomRowStatus.NoDrawing;
if (string.IsNullOrWhiteSpace(Material))
return BomRowStatus.NeedsMaterial;
if (Thickness is not double t || !double.IsFinite(t) || t <= 0)
return BomRowStatus.NeedsThickness;
if (Qty is not int q || q < 1)
return BomRowStatus.NeedsQuantity;
return BomRowStatus.Ready;
}
}
public string StatusText => Describe(Status);
///
/// True when the row has a drawing, so the operator can complete its
/// values; rows without one can never be imported.
///
public bool IsEditable => Status is not (BomRowStatus.NoFileName or BomRowStatus.NoDrawing);
///
/// Sets Qty from typed text when
/// accepts it; otherwise leaves the row unchanged and returns false.
///
public bool TrySetQuantity(string text)
{
if (!BomQuantity.TryParse(text, out var value))
return false;
Qty = value;
return true;
}
public static string Describe(BomRowStatus status) =>
status switch
{
BomRowStatus.Ready => "Ready",
BomRowStatus.NeedsMaterial => "Needs material",
BomRowStatus.NeedsThickness => "Needs thickness",
BomRowStatus.NeedsQuantity => "Needs quantity",
BomRowStatus.NoDrawing => "No drawing found",
BomRowStatus.NoFileName => "No file name",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null),
};
private void Set(ref T field, T value, [CallerMemberName] string name = null)
{
if (EqualityComparer.Default.Equals(field, value))
return;
field = value;
OnPropertyChanged(name);
OnPropertyChanged(nameof(Status));
OnPropertyChanged(nameof(StatusText));
}
private void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}