using Comal.Classes; using InABox.Core; using InABox.Database; using InABox.Scripting; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; namespace PRS.Shared.Events; public class SaveEvent : IEvent> where T : Entity, new() { public Type Entity => typeof(T); public IEventDataModelDefinition DataModelDefinition() { return new SaveEventDataModelDefinition(this); } public Notification GenerateNotification(SaveEventDataModel model) { var notification = new Notification(); notification.Title = $"Updated {typeof(T).Name}"; notification.Description = $"Updated {typeof(T).Name}"; if(model.Entity.ID != Guid.Empty) { notification.EntityType = CoreUtils.EntityName(model.EntityType); notification.EntityID = model.Entity.ID; } return notification; } public void Init(IStore store, IEventData evData, SaveEventDataModel model) { if (model.Entity.ID != Guid.Empty) { var loadCols = Columns.None(); var prefix = $"{typeof(T).Name}."; foreach (var variable in evData.ReferencedVariables) { if (variable.StartsWith(prefix)) { var varName = variable[prefix.Length..]; if (!model.Entity.HasOriginalValue(varName)) { loadCols.Add(varName); } } } var data = store.Provider.Query( new Filter(x => x.ID).IsEqualTo(model.Entity.ID), loadCols); if(data.Rows.Count > 0) { data.Rows[0].FillObject(model.Entity); } } } } public class SaveEventDataModelDefinition(SaveEvent ev) : IEventDataModelDefinition where T : Entity, new() { private IEventVariable[]? variables; public SaveEvent Event { get; set; } = ev; public IEnumerable GetVariables() { if(variables is null) { variables = DatabaseSchema.AllProperties(Event.Entity).Select(x => new StandardEventVariable($"{typeof(T).Name}.{x.Name}", x.PropertyType)).ToArray(); variables.SortBy(x => x.Name); } return variables; } public IEventVariable? GetVariable(string name) { if(variables is null) { var prefix = $"{typeof(T).Name}."; if (name.StartsWith(prefix)) { name = name[prefix.Length..]; var prop = DatabaseSchema.Property(Event.Entity, name); if(prop is null) { return null; } else { return new StandardEventVariable(prop.Name, prop.PropertyType); } } else { return null; } } else { return variables.FirstOrDefault(x => x.Name == name); } } } public class SaveEventDataModel(T entity) : IEventDataModel, ITypedEventDataModel where T : Entity, new() { public T Entity { get; set; } = entity; public Type EntityType => typeof(T); public bool TryGetVariable(string name, out object? value) { var prefix = $"{typeof(T).Name}."; if (name.StartsWith(prefix)) { name = name[prefix.Length..]; var prop = DatabaseSchema.Property(typeof(T), name); if(prop != null) { value = prop.Getter()(Entity); return true; } } value = null; return false; } } #region Triggers [Caption("New Record")] public class CreatedSaveEventTrigger : IEventTrigger, SaveEventDataModel> where T : Entity, new() { public string Description => "New Record"; public IEnumerable ReferencedVariables => []; public bool Check(SaveEventDataModel dataModel) { return dataModel.Entity.HasOriginalValue(x => x.ID); } } [Caption("Property Changed")] public class PropertyChangedSaveEventTrigger : IEventTrigger, SaveEventDataModel> where T : Entity, new() { [JsonIgnore] public IProperty? TriggerProperty { get; set; } [JsonProperty(PropertyName = "TriggerProperty")] private string? _property { get => TriggerProperty?.Name; set { TriggerProperty = value is null ? null : DatabaseSchema.PropertyStrict(typeof(T), value); } } public string Description => TriggerProperty is null ? $"{typeof(T).GetCaption()} changed" : $"{typeof(T).GetCaption()}.{TriggerProperty.Name} changed"; public IEnumerable ReferencedVariables => []; public bool Check(SaveEventDataModel dataModel) { if(TriggerProperty is null) { return false; } if (!dataModel.Entity.HasOriginalValue(TriggerProperty.Name)) { return false; } return true; } } [Caption("Custom Script")] public class ScriptSaveEventTrigger : IEventTrigger, SaveEventDataModel> where T : Entity, new() { public string Description => "Custom Script"; private ScriptDocument? _scriptDocument; private string? _script; public string? Script { get => _script; set { if(_script != value) { _script = value; _scriptDocument = null; } } } public IEnumerable ReferencedVariables => []; public string DefaultScript() { return @"using PRS.Shared.Events; public class Module { public bool Check(SaveEventDataModel<" + typeof(T).Name + @"> model) { // Return true if model.Entity meets the requirements for this event trigger. return true; } }"; } public bool Check(SaveEventDataModel dataModel) { if (Script is null) return false; if(_scriptDocument is null) { _scriptDocument = new(Script); _scriptDocument.Compile(); } return _scriptDocument.Execute(methodname: "Check", parameters: [dataModel]); } } #endregion #region Actions [Caption("Custom Script")] public class ScriptSaveEventAction : IEventAction> where T : Entity, new() { private ScriptDocument? _scriptDocument; private string? _script; public string? Script { get => _script; set { if(_script != value) { _script = value; _scriptDocument = null; } } } public IEnumerable ReferencedVariables => []; public string Description => "Custom Script"; public string DefaultScript() { return @"using PRS.Shared.Events; public class Module { public object? Result { get; set; } public bool Execute(SaveEventDataModel<" + typeof(T).Name + @"> model) { // Do anything you want with model.Entity, and then save return-value to 'Result', or leave it as 'null' if no return value is needed. return true; } }"; } public object? Execute(IEventDataModel dataModel) { if (Script is null) return null; if(_scriptDocument is null) { _scriptDocument = new(Script); _scriptDocument.SetValue("Result", null); _scriptDocument.Compile(); } var model = dataModel.RootModel>(); if(_scriptDocument.Execute(methodname: "Execute", parameters: [model])) { return _scriptDocument.GetValue("Result"); } else { return null; } } } [Caption("Create Entity")] public class CreateEntitySaveEventAction : IEventAction> where T : Entity, new() { [JsonProperty(Order = 1)] public Type? EntityType { get; set; } [JsonIgnore] public List Initializers { get; set; } = new List(); public string Description => $"Create New {EntityType?.Name ?? "Entity"}"; public IEnumerable ReferencedVariables => Initializers.SelectMany(x => x.ReferencedVariables); public object? Execute(IEventDataModel dataModel) { if(EntityType is null) { return null; } var entity = (Activator.CreateInstance(EntityType) as Entity)!; foreach(var initializer in Initializers) { initializer.Execute(entity, dataModel); } DbFactory.FindStore(EntityType, Guid.Empty, "", default, "", Logger.Main).Save(entity, ""); return entity; } #region Serialization Stuff [JsonProperty(Order = 2, PropertyName = "Initializers")] private PropertyInitializerSerializationItem[] _initializers { get => Initializers.ToArray(x => new PropertyInitializerSerializationItem { Property = x.Property.Name, Value = x.Value }); set { Initializers.Clear(); Initializers.AddRange(value.Select(x => new PropertyInitializer(EntityType!, x))); } } #endregion } internal class PropertyInitializerSerializationItem { public string Property { get; set; } public string Value { get; set; } } public class PropertyInitializer { public IProperty Property { get; set; } private CoreExpression? _valueExpression; private CoreExpression ValueExpression { get { _valueExpression ??= new CoreExpression(Value, Property.PropertyType); return _valueExpression; } } private string _value; public string Value { get => _value; [MemberNotNull(nameof(_value))] set { if(value != _value) { _value = value; _valueExpression = null; } } } public IEnumerable ReferencedVariables => ValueExpression.ReferencedVariables; internal PropertyInitializer(Type entityType, PropertyInitializerSerializationItem item) { Value = item.Value; Property = DatabaseSchema.PropertyStrict(entityType, item.Property); } public PropertyInitializer(IProperty property, string value) { Property = property; Value = value; } public void Execute(Entity entity, IEventDataModel dataModel) { Property.Setter()(entity, ValueExpression.Evaluate(dataModel)); } } #endregion