| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 | 
							- using Comal.Classes;
 
- using Expressive;
 
- using InABox.Core;
 
- using InABox.Database;
 
- using Org.BouncyCastle.Asn1.X509.Qualified;
 
- using System;
 
- using System.Collections;
 
- using System.Collections.Generic;
 
- using System.Diagnostics.CodeAnalysis;
 
- using System.Linq;
 
- using System.Reflection;
 
- using System.Text;
 
- using System.Threading.Tasks;
 
- namespace PRS.Shared.Events;
 
- public interface IEventData : ISerializeBinary
 
- {
 
-     IEvent Event { get; }
 
- }
 
- public class EventData<T, TDataModel> : IEventData
 
-     where T : IEvent<TDataModel>
 
-     where TDataModel : IEventDataModel
 
- {
 
-     public T Event { get; set; }
 
-     /// <summary>
 
-     /// A list of triggers for this event. If any of the triggers match, the event runs.
 
-     /// </summary>
 
-     public List<IEventTrigger<T, TDataModel>> Triggers { get; set; }
 
-     public List<IEventAction<T>> Actions { get; set; }
 
-     IEvent IEventData.Event => Event;
 
-     public EventData(T eventData)
 
-     {
 
-         Event = eventData;
 
-         Triggers = new List<IEventTrigger<T, TDataModel>>();
 
-         Actions = new List<IEventAction<T>>();
 
-     }
 
-     public Notification GenerateNotification(TDataModel model) => Event.GenerateNotification(model);
 
-     public void SerializeBinary(CoreBinaryWriter writer)
 
-     {
 
-         Event.SerializeBinary(writer);
 
-         writer.Write(Triggers.Count);
 
-         foreach(var trigger in Triggers)
 
-         {
 
-             EventUtils.SerializeObject(trigger, writer);
 
-         }
 
-         writer.Write(Actions.Count);
 
-         foreach(var action in Actions)
 
-         {
 
-             EventUtils.SerializeObject(action, writer);
 
-         }
 
-     }
 
-     public void DeserializeBinary(CoreBinaryReader reader)
 
-     {
 
-         Event.DeserializeBinary(reader);
 
-         var nTriggers = reader.ReadInt32();
 
-         for(int i = 0; i < nTriggers; ++i)
 
-         {
 
-             var trigger = EventUtils.DeserializeObject<IEventTrigger<T, TDataModel>>(EventUtils.GetTriggerType, reader);
 
-             Triggers.Add(trigger);
 
-         }
 
-         var nActions = reader.ReadInt32();
 
-         for(int i = 0; i < nActions; ++i)
 
-         {
 
-             var action = EventUtils.DeserializeObject<IEventAction<T>>(EventUtils.GetTriggerType, reader);
 
-             Actions.Add(action);
 
-         }
 
-     }
 
- }
 
- public static class EventUtils
 
- {
 
-     #region Serialisation
 
-     public static void SerializeObject(ISerializeBinary obj, CoreBinaryWriter writer)
 
-     {
 
-         writer.Write(obj.GetType().Name);
 
-         foreach(var arg in obj.GetType().GenericTypeArguments)
 
-         {
 
-             writer.Write(CoreUtils.EntityName(arg));
 
-         }
 
-         obj.SerializeBinary(writer);
 
-     }
 
-     public static T DeserializeObject<T>(Func<string, Type> typeSource, CoreBinaryReader reader)
 
-         where T : class, ISerializeBinary
 
-     {
 
-         var eventTypeName = reader.ReadString();
 
-         var objType = typeSource(eventTypeName);
 
-         var typeParams = objType.GetTypeInfo().GenericTypeParameters;
 
-         var typeArgs = new Type[typeParams.Length];
 
-         for(int i = 0; i < typeParams.Length; ++i)
 
-         {
 
-             var entityType = CoreUtils.GetEntity(reader.ReadString());
 
-             typeArgs[i] = entityType;
 
-         }
 
-         if(typeArgs.Length > 0)
 
-         {
 
-             objType = objType.MakeGenericType(typeArgs);
 
-         }
 
-         var obj = (Activator.CreateInstance(objType) as T)!;
 
-         obj.DeserializeBinary(reader);
 
-         return obj;
 
-     }
 
-     public static void Serialize(IEventData data, CoreBinaryWriter writer)
 
-     {
 
-         writer.Write(data.Event.GetType().Name);
 
-         foreach(var arg in data.Event.GetType().GenericTypeArguments)
 
-         {
 
-             writer.Write(CoreUtils.EntityName(arg));
 
-         }
 
-         data.SerializeBinary(writer);
 
-     }
 
-     public static IEventData Deserialize(CoreBinaryReader reader)
 
-     {
 
-         var eventTypeName = reader.ReadString();
 
-         var eventType = EventUtils.GetEventType(eventTypeName);
 
-         var typeParams = eventType.GetTypeInfo().GenericTypeParameters;
 
-         var typeArgs = new Type[typeParams.Length];
 
-         for(int i = 0; i < typeParams.Length; ++i)
 
-         {
 
-             var entityType = CoreUtils.GetEntity(reader.ReadString());
 
-             typeArgs[i] = entityType;
 
-         }
 
-         if(typeArgs.Length > 0)
 
-         {
 
-             eventType = eventType.MakeGenericType(typeArgs);
 
-         }
 
-         var ev = (Activator.CreateInstance(eventType) as IEvent)!;
 
-         var dataModelType = ev.GetType().GetInterfaceDefinition(typeof(IEvent<>))!.GenericTypeArguments[0];
 
-         var eventDataType = typeof(EventData<,>).MakeGenericType(ev.GetType(), dataModelType);
 
-         var eventData = (Activator.CreateInstance(eventDataType, ev) as IEventData)!;
 
-         eventData.DeserializeBinary(reader);
 
-         return eventData;
 
-     }
 
-     #endregion
 
-     #region Event Types
 
-     private static Dictionary<string, Type>? _eventTypes;
 
-     private static Dictionary<string, Type>? _triggerTypes;
 
-     private static Dictionary<string, Type>? _actionTypes;
 
-     private static Dictionary<Type, List<Type>>? _eventTriggerTypes;
 
-     private static Dictionary<Type, List<Type>>? _eventActionTypes;
 
-     [MemberNotNullWhen(true, nameof(_eventTypes), nameof(_triggerTypes), nameof(_actionTypes), nameof(_eventTriggerTypes), nameof(_eventActionTypes))]
 
-     private static bool _loadedTypes { get; set; }
 
-     [MemberNotNull(nameof(_eventTypes), nameof(_triggerTypes), nameof(_actionTypes), nameof(_eventTriggerTypes), nameof(_eventActionTypes))]
 
-     private static void LoadTypes()
 
-     {
 
-         if (_loadedTypes) return;
 
-         _loadedTypes = true;
 
-         _eventTypes = new();
 
-         _triggerTypes = new();
 
-         _actionTypes = new();
 
-         _eventTriggerTypes = new();
 
-         _eventActionTypes = new();
 
-         foreach(var type in CoreUtils.TypeList(x => true))
 
-         {
 
-             if (type.HasInterface(typeof(IEvent<>)))
 
-             {
 
-                 if (type.GetConstructor([]) is not null)
 
-                 {
 
-                     _eventTypes[type.Name] = type;
 
-                 }
 
-             }
 
-             else if (type.GetInterfaceDefinition(typeof(IEventTrigger<,>)) is Type eventTriggerInterface)
 
-             {
 
-                 if (type.GetConstructor([]) is not null)
 
-                 {
 
-                     _triggerTypes[type.Name] = type;
 
-                     var eventType = eventTriggerInterface.GenericTypeArguments[0];
 
-                     eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
 
-                     _eventTriggerTypes.GetValueOrAdd(eventType).Add(type);
 
-                 }
 
-             }
 
-             else if (type.GetInterfaceDefinition(typeof(IEventAction<>)) is Type eventActionInterface)
 
-             {
 
-                 if (type.GetConstructor([]) is not null)
 
-                 {
 
-                     _actionTypes[type.Name] = type;
 
-                     var eventType = eventActionInterface.GenericTypeArguments[0];
 
-                     eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
 
-                     _eventActionTypes.GetValueOrAdd(eventType).Add(type);
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     public static IEnumerable<Type> GetEventTriggerTypes(Type eventType)
 
-     {
 
-         LoadTypes();
 
-         eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
 
-         return _eventTriggerTypes.GetValueOrDefault(eventType) ?? Enumerable.Empty<Type>();
 
-     }
 
-     public static IEnumerable<Type> GetEventActionTypes(Type eventType)
 
-     {
 
-         LoadTypes();
 
-         eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
 
-         return _eventActionTypes.GetValueOrDefault(eventType) ?? Enumerable.Empty<Type>();
 
-     }
 
-     public static Type GetEventType(string type)
 
-     {
 
-         LoadTypes();
 
-         return _eventTypes[type]; // Force exception if not a valid type name.
 
-     }
 
-     public static Type GetTriggerType(string type)
 
-     {
 
-         LoadTypes();
 
-         return _triggerTypes[type]; // Force exception if not a valid type name.
 
-     }
 
-     public static Type GetActionType(string type)
 
-     {
 
-         LoadTypes();
 
-         return _actionTypes[type]; // Force exception if not a valid type name.
 
-     }
 
-     #endregion
 
-     #region Execution
 
-     private static bool Check<T, TDataModel>(EventData<T, TDataModel> ev, TDataModel dataModel)
 
-         where T : IEvent<TDataModel>
 
-         where TDataModel : IEventDataModel
 
-     {
 
-         return ev.Triggers.Any(x => x.Check(dataModel));
 
-     }
 
-     public static void Run<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
 
-         where T : IEvent<TDataModel>
 
-         where TDataModel : IEventDataModel
 
-     {
 
-         if (!Check(evData, dataModel)) return;
 
-         foreach(var action in evData.Actions)
 
-         {
 
-             IEvent.RunAction(evData.Event, dataModel, action);
 
-         }
 
-         NotifySubscribers(store, ev, evData, dataModel);
 
-     }
 
-     private static void NotifySubscribers<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
 
-         where T : IEvent<TDataModel>
 
-         where TDataModel : IEventDataModel
 
-     {
 
-         string? description;
 
-         if (ev.NotificationExpression.IsNullOrWhiteSpace())
 
-         {
 
-             description = null;
 
-         }
 
-         else
 
-         {
 
-             var descriptionExpr = new CoreExpression(ev.NotificationExpression, typeof(string));
 
-             if(!descriptionExpr.TryEvaluate<string>(dataModel).Get(out description, out var error))
 
-             {
 
-                 CoreUtils.LogException(store.UserID, error, extra: "Error notifying subscribers", store.Logger);
 
-                 return;
 
-             }
 
-         }
 
-         var subscribers = store.Provider.Query(
 
-             new Filter<EventSubscriber>(x => x.Event.ID).IsEqualTo(ev.ID),
 
-             Columns.None<EventSubscriber>().Add(x => x.Employee.ID))
 
-             .ToArray<EventSubscriber>();
 
-         var notifications = new List<Notification>();
 
-         foreach(var subscriber in subscribers)
 
-         {
 
-             var notification = evData.GenerateNotification(dataModel);
 
-             notification.Employee.CopyFrom(subscriber.Employee);
 
-             if(description is not null)
 
-             {
 
-                 notification.Description = description;
 
-             }
 
-             notifications.Add(notification);
 
-         }
 
-         store.Provider.Save(notifications);
 
-     }
 
-     #endregion
 
- }
 
- #region DataModel Definition
 
- public interface IEventVariable
 
- {
 
-     string Name { get; set; }
 
-     Type Type { get; set; }
 
- }
 
- public class StandardEventVariable : IEventVariable
 
- {
 
-     public string Name { get; set; }
 
-     public Type Type { get; set; }
 
-     public StandardEventVariable(string name, Type type)
 
-     {
 
-         Name = name;
 
-         Type = type;
 
-     }
 
- }
 
- public class ListEventVariable : IEventVariable
 
- {
 
-     public string Name { get; set; }
 
-     public Type Type { get; set; }
 
-     public ListEventVariable(string name, Type type)
 
-     {
 
-         Name = name;
 
-         Type = type;
 
-     }
 
- }
 
- public interface IEventDataModelDefinition
 
- {
 
-     IEnumerable<IEventVariable> GetVariables();
 
-     IEventVariable? GetVariable(string name);
 
- }
 
- #endregion
 
- #region DataModel
 
- public interface IEventDataModel : IVariableProvider
 
- {
 
-     bool TryGetVariable(string name, out object? value);
 
-     bool IVariableProvider.TryGetValue(string variableName, out object? value)
 
-     {
 
-         return TryGetVariable(variableName, out value);
 
-     }
 
-     T RootModel<T>()
 
-         where T : IEventDataModel
 
-     {
 
-         if(this is IChildEventDataModel child)
 
-         {
 
-             return child.Parent.RootModel<T>();
 
-         }
 
-         else if(this is T root)
 
-         {
 
-             return root;
 
-         }
 
-         else
 
-         {
 
-             throw new Exception($"Root model of wrong type; expected {typeof(T).FullName}; got {GetType().FullName}");
 
-         }
 
-     }
 
- }
 
- public interface ITypedEventDataModel : IEventDataModel
 
- {
 
-     public Type EntityType { get; }
 
- }
 
- public interface IChildEventDataModel : IEventDataModel
 
- {
 
-     IEventDataModel Parent { get; }
 
- }
 
- public class ChildEventDataModel : IChildEventDataModel
 
- {
 
-     public IEventDataModel Parent { get; set; }
 
-     public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
 
-     public ChildEventDataModel(IEventDataModel parent)
 
-     {
 
-         Parent = parent;
 
-     }
 
-     public bool TryGetVariable(string name, out object? value)
 
-     {
 
-         return Values.TryGetValue(name, out value)
 
-             || Parent.TryGetVariable(name, out value);
 
-     }
 
- }
 
- #endregion
 
- public interface IEvent : ISerializeBinary
 
- {
 
-     IEventDataModelDefinition DataModelDefinition();
 
-     public static void RunAction(IEvent ev, IEventDataModel model, IEventAction action)
 
-     {
 
-         var dataModelDef = ev.DataModelDefinition();
 
-         var values = new List<(string name, object? value)[]>() { Array.Empty<(string, object?)>() };
 
-         var vars = action.ReferencedVariables();
 
-         foreach(var variable in vars)
 
-         {
 
-             var varDef = dataModelDef.GetVariable(variable);
 
-             if(varDef is ListEventVariable)
 
-             {
 
-                 if (model.TryGetVariable(varDef.Name, out var list) && list is IEnumerable enumerable)
 
-                 {
 
-                     var oldValues = values;
 
-                     values = new List<(string name, object? value)[]>();
 
-                     foreach(var item in enumerable)
 
-                     {
 
-                         foreach(var valueList in oldValues)
 
-                         {
 
-                             values.Add(valueList.Concatenate(new (string name, object? value)[]
 
-                             {
 
-                                 (varDef.Name, item)
 
-                             }));
 
-                         }
 
-                     }
 
-                 }
 
-                 else
 
-                 {
 
-                     values.Clear();
 
-                     break;
 
-                 }
 
-             }
 
-         }
 
-         if(values.Count > 0 && (values.Count > 1 || values[0].Length > 0))
 
-         {
 
-             var subModel = new ChildEventDataModel(model);
 
-             foreach(var valueSet in values)
 
-             {
 
-                 subModel.Values.Clear();
 
-                 foreach(var (name, value) in valueSet)
 
-                 {
 
-                     subModel.Values[name] = value;
 
-                 }
 
-                 action.Execute(subModel);
 
-             }
 
-         }
 
-         else
 
-         {
 
-             action.Execute(model);
 
-         }
 
-     }
 
- }
 
- public interface IEvent<TDataModel> : IEvent
 
- {
 
-     Notification GenerateNotification(TDataModel model);
 
- }
 
- public interface IEventTrigger
 
- {
 
-     string GetDescription();
 
- }
 
- public interface IEventTrigger<TEvent, TDataModel> : ISerializeBinary, IEventTrigger
 
-     where TEvent : IEvent<TDataModel>
 
-     where TDataModel : IEventDataModel
 
- {
 
-     bool Check(TDataModel dataModel);
 
- }
 
- public interface IEventAction : ISerializeBinary
 
- {
 
-     IEnumerable<string> ReferencedVariables();
 
-     object? Execute(IEventDataModel dataModel);
 
-     string GetDescription();
 
- }
 
- public interface IEventAction<TEvent> : IEventAction
 
-     where TEvent : IEvent
 
- {
 
- }
 
 
  |