using InABox.Core; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq.Expressions; namespace Comal.Classes { public abstract class Dimensions : EnclosedEntity, IDimensions where TLink : DimensionUnitLink, new() where TUnit : DimensionUnit, new() { [EditorSequence(1)] [RequiredColumn] [Caption("Sizing", IncludePath = false)] public abstract TLink Unit { get; set; } public IDimensionUnit GetUnit() => Unit; [DoubleEditor(Visible = Visible.Hidden)] [EditorSequence(2)] [Caption("Quantity", IncludePath = false)] [RequiredColumn] public abstract double Quantity { get; set; } [DoubleEditor(Visible = Visible.Hidden)] [EditorSequence(3)] [Caption("Length", IncludePath = false)] [RequiredColumn] public abstract double Length { get; set; } [DoubleEditor(Visible = Visible.Hidden)] [EditorSequence(4)] [Caption("Width", IncludePath = false)] [RequiredColumn] public abstract double Width { get; set; } [DoubleEditor(Visible = Visible.Hidden)] [EditorSequence(5)] [Caption("Height", IncludePath = false)] [RequiredColumn] public abstract double Height { get; set; } [DoubleEditor(Visible = Visible.Hidden)] [EditorSequence(6)] [Caption("Weight", IncludePath = false)] [RequiredColumn] public abstract double Weight { get; set; } [DoubleEditor(Visible = Visible.Optional, Editable = Editable.Hidden)] [Caption("Value", IncludePath = false)] [EditorSequence(7)] [RequiredColumn] public abstract double Value { get; set; } [TextBoxEditor(Visible = Visible.Default, Editable = Editable.Hidden)] [EditorSequence(8)] [Caption("Unit Size", IncludePath = false)] [RequiredColumn] public abstract String UnitSize { get; set; } IDimensionUnit IDimensions.Unit => Unit; protected override void Init() { base.Init(); Unit.PropertyChanged += (s, e) => { if (e.PropertyName == "ID") { DoPropertyChanged("Unit." + e.PropertyName, OriginalValues.GetValueOrDefault("Unit." + e.PropertyName), Unit.ID); } else if (e.PropertyName == "Formula") { DoPropertyChanged("Unit." + e.PropertyName, OriginalValues.GetValueOrDefault("Unit." + e.PropertyName), Unit.Formula); } else if (e.PropertyName == "Format") { DoPropertyChanged("Unit." + e.PropertyName, OriginalValues.GetValueOrDefault("Unit." + e.PropertyName), Unit.Format); } }; } private bool bCalculating = false; private static Column> unitid = new Column>(x => x.Unit.ID); private static Column> quantity = new Column>(x => x.Quantity); private static Column> length = new Column>(x => x.Length); private static Column> width = new Column>(x => x.Width); private static Column> height = new Column>(x => x.Height); private static Column> weight = new Column>(x => x.Weight); private static Column> sizeformula = new Column>(x => x.Unit.Formula); private static Column> sizeformat = new Column>(x => x.Unit.Format); protected override void DoPropertyChanged(string name, object? before, object? after) { base.DoPropertyChanged(name, before, after); if (bCalculating) return; bCalculating = true; try { if (unitid.IsEqualTo(name)) Calculate(Quantity, Length, Width, Height, Weight, Unit.Formula, Unit.Format); else if (quantity.IsEqualTo(name)) Calculate(after, Length, Width, Height, Weight, Unit.Formula, Unit.Format); else if (length.IsEqualTo(name)) Calculate(Quantity, after, Width, Height, Weight, Unit.Formula, Unit.Format); else if (width.IsEqualTo(name)) Calculate(Quantity, Length, after, Height, Weight, Unit.Formula, Unit.Format); else if (height.IsEqualTo(name)) Calculate(Quantity, Length, Width, after, Weight, Unit.Formula, Unit.Format); else if (weight.IsEqualTo(name)) Calculate(Quantity, Length, Width, Height, after, Unit.Formula, Unit.Format); else if (sizeformula.IsEqualTo(name)) Calculate(Quantity, Length, Width, Height, Weight, after as string, Unit.Format); else if (sizeformat.IsEqualTo(name)) Calculate(Quantity, Length, Width, Height, Weight, Unit.Formula, after as string); } finally { bCalculating = false; } } public void Set(IDimensionUnit unit, double quantity, double length, double width, double height, double weight) { bCalculating = true; try { Unit.ID = unit.ID; Unit.HasQuantity = unit.HasQuantity; Unit.HasLength = unit.HasLength; Unit.HasWidth = unit.HasWidth; Unit.HasHeight = unit.HasHeight; Unit.HasWeight = unit.HasWeight; Unit.Code = unit.Code; Unit.Description = unit.Description; Unit.Formula = unit.Formula; Unit.Format = unit.Format; Quantity = quantity; Length = length; Width = width; Height = height; Weight = weight; Calculate(quantity, length, width, height, weight, unit.Formula, unit.Format); } finally { bCalculating = false; } } public void CalculateValueAndUnitSize() { Calculate(Quantity, Length, Width, Height, Weight, Unit.Formula, Unit.Format); } private void Calculate(object? quantity, object? length, object? width, object? height, object? weight, string? formula, string? format) { if (Evaluate(formula, quantity, length, width, height, weight, out double value)) Value = value; if (Evaluate(format, quantity, length, width, height, weight, out string unitsize)) UnitSize = unitsize; } private bool Evaluate(string? formula, object? quantity, object? length, object? width, object? height, object? weight, out T result) { if (!String.IsNullOrWhiteSpace(formula)) { var variables = new Dictionary() { { "Quantity", Convert.ToDouble(quantity) }, { "Length", Convert.ToDouble(length) }, { "Width", Convert.ToDouble(width) }, { "Height", Convert.ToDouble(height) }, { "Weight", Convert.ToDouble(weight) } }; try { var expr = new CoreExpression(formula); var eval = expr.Evaluate(variables); result = (T)CoreUtils.ChangeType(eval, typeof(T)); return true; } catch (Exception e) { Logger.Send(LogType.Information, "", String.Format("Error in Formula: [{0}] ({1})", formula, e.Message)); result = default(T); return false; } } result = default(T); return true; } public void CopyFrom(IDimensions source, bool observing = false) { if (!observing) SetObserving(false); Unit.ID = source.GetUnit().ID; Unit.Synchronise(source.GetUnit()); Quantity = source.Quantity; Length = source.Length; Width = source.Width; Height = source.Height; Weight = source.Weight; Value = source.Value; UnitSize = source.UnitSize; if (!observing) SetObserving(true); } public override string ToString() { var result = Value != 0 ? Math.Round(Value, 3).ToString() : ""; result = !string.IsNullOrWhiteSpace(UnitSize) ? result + " " + UnitSize + ", " : "Empty unitsize"; result = Quantity != 0 ? result + "Quantity: " + Quantity + ", " : result; result = Length != 0 ? result + "Length: " + Length + ", " : result; result = Width != 0 ? result + "Width: " + Width + ", " : result; result = Height != 0 ? result + "Height: " + Height + ", " : result; result = Weight != 0 ? result + "Weight: " + Weight : result; if (result.EndsWith(", ")) result = result.Remove(result.Length - 2); return result; } public override bool Equals(object obj) { return obj is IDimensions dim && Unit.ID == dim.Unit.ID && Quantity.IsEffectivelyEqual(dim.Quantity) && Length.IsEffectivelyEqual(dim.Length) && Width.IsEffectivelyEqual(dim.Width) && Height.IsEffectivelyEqual(dim.Height) && Weight.IsEffectivelyEqual(dim.Weight); } public override int GetHashCode() { return HashCode.Combine( Unit.ID, Quantity, Length, Width, Height, Weight); } } public static class Dimensions { private static readonly Column unitid = new Column(x => x.Unit.ID); private static readonly Column quantity = new Column(x => x.Quantity); private static readonly Column length = new Column(x => x.Length); private static readonly Column width = new Column(x => x.Width); private static readonly Column height = new Column(x => x.Height); private static readonly Column weight = new Column(x => x.Weight); public static IEnumerable> GetFilterColumns() { yield return unitid; yield return quantity; yield return length; yield return width; yield return height; yield return weight; } public static Filter DimensionEquals(this Filter filter, IDimensions dim) { if (!CoreUtils.TryFindMemberExpression(filter.Expression, out var mexp)) { throw new ArgumentException("Filter expression is not a MemberExpression"); } var prop = CoreUtils.GetFullPropertyName(mexp, ".") + "."; filter.Expression = CoreUtils.GetMemberExpression(typeof(T), prop + unitid.Property); filter.IsEqualTo(dim.Unit.ID); filter.And(prop + quantity.Property).IsEqualTo(dim.Quantity); filter.And(prop + length.Property).IsEqualTo(dim.Length); filter.And(prop + width.Property).IsEqualTo(dim.Width); filter.And(prop + height.Property).IsEqualTo(dim.Height); filter.And(prop + weight.Property).IsEqualTo(dim.Weight); return filter.Parent ?? filter; } public static IEnumerable>, Expression>>> GetLinks() where T : IDimensioned where U : IDimensioned { return GetLinks(x => x.Dimensions, x => x.Dimensions); } public static IEnumerable>, Expression>>> GetLinks( Expression> tDimensions, Expression> uDimensions ) { var tPrefix = CoreUtils.GetFullPropertyName(tDimensions, ".") + "."; var uPrefix = CoreUtils.GetFullPropertyName(uDimensions, ".") + "."; yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + unitid.Property), CoreUtils.GetPropertyExpression(uPrefix + unitid.Property)); yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + quantity.Property), CoreUtils.GetPropertyExpression(uPrefix + quantity.Property)); yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + length.Property), CoreUtils.GetPropertyExpression(uPrefix + length.Property)); yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + width.Property), CoreUtils.GetPropertyExpression(uPrefix + width.Property)); yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + height.Property), CoreUtils.GetPropertyExpression(uPrefix + height.Property)); yield return new KeyValuePair>, Expression>>( CoreUtils.GetPropertyExpression(tPrefix + weight.Property), CoreUtils.GetPropertyExpression(uPrefix + weight.Property)); } public enum ColumnsType { Local, Data, All } public static int[] GetFilterColumnIndices(Columns columns, Expression> dimensions) { var dimCol = CoreUtils.GetFullPropertyName(dimensions, ".") + "."; return new int[] { columns.IndexOf(dimCol + unitid.Property), columns.IndexOf(dimCol + quantity.Property), columns.IndexOf(dimCol + length.Property), columns.IndexOf(dimCol + width.Property), columns.IndexOf(dimCol + height.Property), columns.IndexOf(dimCol + weight.Property) }; } public static int[] GetFilterColumnIndices(CoreTable table, Expression> dimensions) { var dimCol = CoreUtils.GetFullPropertyName(dimensions, ".") + "."; return new int[] { table.GetColumnIndex(dimCol + unitid.Property), table.GetColumnIndex(dimCol + quantity.Property), table.GetColumnIndex(dimCol + length.Property), table.GetColumnIndex(dimCol + width.Property), table.GetColumnIndex(dimCol + height.Property), table.GetColumnIndex(dimCol + weight.Property) }; } public static TDim ToDimensions(this CoreRow row, int[] columnIndices) where TDim : IDimensions, new() { var dimensions = new TDim(); dimensions.Unit.ID = row.Get(columnIndices[0]); dimensions.Quantity = row.Get(columnIndices[1]); dimensions.Length = row.Get(columnIndices[2]); dimensions.Width = row.Get(columnIndices[3]); dimensions.Height = row.Get(columnIndices[4]); dimensions.Weight = row.Get(columnIndices[5]); return dimensions; } public static TDim ToDimensions(this CoreRow row, Expression> dim) where TDim : IDimensions, new() { var dimensions = new TDim(); var dimCol = CoreUtils.GetFullPropertyName(dim, ".") + "."; dimensions.Unit.ID = row.Get(dimCol + unitid.Property); dimensions.Length = row.Get(dimCol + length.Property); dimensions.Quantity = row.Get(dimCol + quantity.Property); dimensions.Width = row.Get(dimCol + width.Property); dimensions.Height = row.Get(dimCol + height.Property); dimensions.Weight = row.Get(dimCol + weight.Property); return dimensions; } public static Columns LocalColumns() where T : IDimensions { return Columns.None().Add(x => x.Unit.ID) .Add(x => x.Quantity) .Add(x => x.Length) .Add(x => x.Width) .Add(x => x.Height) .Add(x => x.Weight) .Add(x => x.UnitSize) .Add(x => x.Value); } public static Columns DataColumns() where T : IDimensions { return Columns.None().Add(x => x.Unit.ID) .Add(x => x.Unit.ID) .Add(x => x.Unit.Format) .Add(x => x.Unit.Formula) .Add(x => x.Unit.HasHeight) .Add(x => x.Unit.HasWeight) .Add(x => x.Unit.HasWidth) .Add(x => x.Unit.HasQuantity) .Add(x => x.Unit.HasLength) .Add(x => x.Quantity) .Add(x => x.Length) .Add(x => x.Width) .Add(x => x.Height) .Add(x => x.Weight) .Add(x => x.UnitSize) .Add(x => x.Value); } public static Columns AllColumns() where T : IDimensions { return Columns.None().Add(x => x.Unit.ID) .Add(x => x.Unit.ID) .Add(x => x.Unit.Format) .Add(x => x.Unit.Formula) .Add(x => x.Unit.HasHeight) .Add(x => x.Unit.HasWeight) .Add(x => x.Unit.HasWidth) .Add(x => x.Unit.HasQuantity) .Add(x => x.Unit.HasLength) .Add(x => x.Unit.Code) .Add(x => x.Unit.Description) .Add(x => x.Quantity) .Add(x => x.Length) .Add(x => x.Width) .Add(x => x.Height) .Add(x => x.Weight) .Add(x => x.UnitSize) .Add(x => x.Value); } public static Columns AddDimensionsColumns(this Columns columns, Expression> dim, ColumnsType type = ColumnsType.Local) where TDim : IDimensions { return columns.AddSubColumns(dim, type switch { ColumnsType.Data => DataColumns(), ColumnsType.All => AllColumns(), ColumnsType.Local => LocalColumns(), _ => LocalColumns() }); } } }