using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
{
///
/// A group containing zero to many s which it evaluates using a boolean specific
/// operator
///
public class DataModelConditionGroup : DataModelConditionPart
{
private bool _disposed;
///
/// Creates a new instance of the class
///
///
public DataModelConditionGroup(DataModelConditionPart? parent)
{
Parent = parent;
Entity = new DataModelConditionGroupEntity();
ChildAdded += OnChildrenChanged;
ChildRemoved += OnChildrenChanged;
}
///
/// Creates a new instance of the class
///
///
///
public DataModelConditionGroup(DataModelConditionPart? parent, DataModelConditionGroupEntity entity)
{
Parent = parent;
Entity = entity;
BooleanOperator = (BooleanOperator) Entity.BooleanOperator;
foreach (DataModelConditionPartEntity childEntity in Entity.Children)
{
if (childEntity is DataModelConditionGroupEntity groupEntity)
AddChild(new DataModelConditionGroup(this, groupEntity));
else if (childEntity is DataModelConditionListEntity listEntity)
AddChild(new DataModelConditionList(this, listEntity));
else if (childEntity is DataModelConditionEventEntity eventEntity)
AddChild(new DataModelConditionEvent(this, eventEntity));
else if (childEntity is DataModelConditionGeneralPredicateEntity predicateEntity)
AddChild(new DataModelConditionGeneralPredicate(this, predicateEntity));
else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity)
AddChild(new DataModelConditionListPredicate(this, listPredicateEntity));
else if (childEntity is DataModelConditionEventPredicateEntity eventPredicateEntity)
AddChild(new DataModelConditionEventPredicate(this, eventPredicateEntity));
}
ContainsEvents = Children.Any(c => c is DataModelConditionEvent);
ChildAdded += OnChildrenChanged;
ChildRemoved += OnChildrenChanged;
}
///
/// Gets or sets the boolean operator of this group
///
public BooleanOperator BooleanOperator { get; set; }
///
/// Gets whether this group contains any events
///
public bool ContainsEvents { get; private set; }
internal DataModelConditionGroupEntity Entity { get; set; }
///
public override bool Evaluate()
{
if (_disposed)
throw new ObjectDisposedException("DataModelConditionGroup");
// Empty groups are always true
if (Children.Count == 0)
return true;
// Groups with only one child ignore the boolean operator
if (Children.Count == 1)
return Children[0].Evaluate();
if (ContainsEvents)
{
bool eventTriggered = Children.Where(c => c is DataModelConditionEvent).Any(c => c.Evaluate());
return eventTriggered && EvaluateWithOperator(Children.Where(c => !(c is DataModelConditionEvent)));
}
return EvaluateWithOperator(Children);
}
private bool EvaluateWithOperator(IEnumerable targets)
{
return BooleanOperator switch
{
BooleanOperator.And => targets.All(c => c.Evaluate()),
BooleanOperator.Or => targets.Any(c => c.Evaluate()),
BooleanOperator.AndNot => targets.All(c => !c.Evaluate()),
BooleanOperator.OrNot => targets.Any(c => !c.Evaluate()),
_ => throw new ArgumentOutOfRangeException()
};
}
#region IDisposable
///
protected override void Dispose(bool disposing)
{
_disposed = true;
foreach (DataModelConditionPart child in Children)
child.Dispose();
base.Dispose(disposing);
}
#endregion
///
internal override bool EvaluateObject(object? target)
{
if (_disposed)
throw new ObjectDisposedException("DataModelConditionGroup");
// Empty groups are always true
if (Children.Count == 0)
return true;
// Groups with only one child ignore the boolean operator
if (Children.Count == 1)
return Children[0].EvaluateObject(target);
return BooleanOperator switch
{
BooleanOperator.And => Children.All(c => c.EvaluateObject(target)),
BooleanOperator.Or => Children.Any(c => c.EvaluateObject(target)),
BooleanOperator.AndNot => Children.All(c => !c.EvaluateObject(target)),
BooleanOperator.OrNot => Children.Any(c => !c.EvaluateObject(target)),
_ => throw new ArgumentOutOfRangeException()
};
}
internal override void Save()
{
Entity.BooleanOperator = (int) BooleanOperator;
Entity.Children.Clear();
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (DataModelConditionPart child in Children)
child.Save();
}
internal override DataModelConditionPartEntity GetEntity()
{
return Entity;
}
private void OnChildrenChanged(object? sender, EventArgs e)
{
ContainsEvents = Children.Any(c => c is DataModelConditionEvent);
}
}
///
/// Represents a boolean operator
///
public enum BooleanOperator
{
///
/// All the conditions in the group should evaluate to true
///
And,
///
/// Any of the conditions in the group should evaluate to true
///
Or,
///
/// All the conditions in the group should evaluate to false
///
AndNot,
///
/// Any of the conditions in the group should evaluate to false
///
OrNot
}
}