using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using Chernobyl.Collections.Generic; using Chernobyl.Destruction; using Enumerable = System.Linq.Enumerable; namespace Chernobyl { /// /// Encapsulates a method that has four parameters and does not return a value. /// public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); /// /// Utility and extension methods for the action delegates. /// public static class ActionExt { /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Action This(Action action) => action; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Action This(Action action) => action; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Action This(Action action) => action; /// /// Joins together multiple actions into a single action. /// public static Action Join(IEnumerable> actions) => arg1 => actions.For(a => a(arg1)); /// /// Joins together multiple actions into a single action. /// public static Action Join(params Action[] actions) => Join(actions.AsEnumerable()); /// /// Invokes all of the methods in the list. /// public static void Invoke(this IEnumerable actions) => actions.For(a => a()); /// /// Invokes all of the methods in the list with the provided argument. /// public static void Invoke(this IEnumerable> actions, T1 arg1) => actions.For(a => a(arg1)); /// /// Returns a method that projects the argument given to it using /// before passing the projection onto . /// public static Action Select(this Action action, Func selector) => arg1 => action(selector(arg1)); /// /// Returns a method that invokes with its first argument replaced /// by the result of . /// public static Action SelectArg1(this Action action, Func selector) => (arg1, arg2) => action(selector(arg1), arg2); /// /// Returns a method that invokes with its second argument replaced /// by the result of . /// public static Action SelectArg2(this Action action, Func selector) => (arg1, arg2) => action(arg1, selector(arg2)); /// /// Returns a method that, when invoked, invokes but only if /// returns true. /// public static Action Where(this Action action, Func predicate) => v => { if (predicate(v)) action(v); }; /// /// Returns a method that, when invoked, invokes but only if /// returns true. /// public static Action Where(this Action action, Func predicate) { return (arg1, arg2) => { if (predicate(arg1, arg2)) action(arg1, arg2); }; } /// /// Returns a method that, when invoked, invokes but only if /// returns true. /// public static Action WhereArg2(this Action action, Func predicate) { return (arg1, arg2) => { if (predicate(arg2)) action(arg1, arg2); }; } /// /// Returns a method that, when invoked, invokes but only if /// returns true. /// public static Action Where(this Action action, Func predicate) { return (arg1, arg2, arg3) => { if (predicate(arg1, arg2, arg3)) action(arg1, arg2, arg3); }; } /// /// Returns a method that uses the provided value as the second argument to the provided method. /// public static Action BindArg1(this Action action, Arg arg1) => arg2 => action(arg1.Func(), arg2); /// /// Returns a method that uses the provided value as the second argument to the provided method. /// public static Action BindArg2(this Action action, Arg arg2) => arg1 => action(arg1, arg2.Func()); /// /// Returns a method two parameter that invokes with the second /// argument but not the first. /// public static Action PrependArg(this Action action) => (arg1, arg2) => action(arg2); /// /// Returns a no-operation . /// public static Action None() => arg1 => {}; /// /// Returns a no-operation . /// public static Action None() => (arg1, arg2) => {}; } /// /// An instance that can be converted to an that, when invoked, /// invokes a set of and allows removal from that list when /// dictated by an . This type is thread safe. /// public class CompositeAction : CompositeAction { /// /// Adds an to be updated which can be removed by calling /// on the returned instance. This method and the returned /// is thread safe. /// public IDisposable Register(Action action) => Register(arg1 => action()); /// /// Converts this instance to an so that can be invoked like one. /// public static implicit operator Action(CompositeAction a) => a.Invoke; /// /// Invokes all of the instances. This call is thread safe. /// public void Invoke() => Invoke(0); } /// /// An instance that can be converted to an that, when invoked, /// invokes a set of and allows removal from that list when /// dictated by an . This type is thread safe. /// public class CompositeAction { /// /// Adds an to be updated which can be removed by calling /// on the returned instance. This method and the returned /// is thread safe. /// public IDisposable Register(Action action) { _actions.TryAdd(action, action); return ActionExt.This(() => _actions.TryRemove(action, out _)).AsDisposable(); } /// /// Converts this instance to an so that can be invoked like one. /// public static implicit operator Action(CompositeAction action) => action.Action; /// /// Invokes all of the instances. This call is thread safe. /// public void Invoke(T1 arg1) => _actions.For(a => a.Value(arg1)); /// /// Performs the same function as . This property makes fluent /// programming easier. /// public Action Action => Invoke; // Using ConcurrentDictionary because it is the only concurrent collection that allows for // removal by value (i.e. TryRemove). readonly ConcurrentDictionary, Action> _actions = new ConcurrentDictionary, Action>(); } /// /// An functor that holds onto the last value it was given. This type /// can be implicitly converted to an . /// /// This type is NOT thread safe. public class LastValue { /// /// The last value given to the this . /// public T Value { get; private set; } /// /// Converts this functor to an . /// public static implicit operator Action(LastValue lv) => lv.Accept; /// /// The that accepts the value. /// public void Accept(T value) => Value = value; } /// /// An functor that toggles between two states. This type /// can be implicitly converted to an . /// /// This type is NOT thread safe. public class Toggle { /// /// Constructs the toggle with an initial state of false. /// public Toggle() : this(false, null) { } /// The initial state of this instance. /// Method to invoke when value is toggled. public Toggle(bool state, Action onStateChange = null) { State = state; _onStateChange = onStateChange ?? ActionExt.None(); } /// /// The current state of this instance. /// public bool State { get; private set; } /// /// Converts this functor to an . /// public static implicit operator Action(Toggle t) => t.Invoke; /// /// The that accepts the value. /// public void Invoke() { State = !State; _onStateChange(State); } readonly Action _onStateChange; } /// /// An functor that can be disposed of. /// public class DisposableAction : IDisposable { /// The action to be invoked. /// The result of disposing the action. public DisposableAction(Action action, Action onDispose) { Action = action; _onDispose = onDispose; } /// /// Projects this functor into another form. /// /// The method that projects the action. /// The new form of the functor. public DisposableAction Select(Func, Action> selector) => new DisposableAction(selector(Action), _onDispose); /// /// Converts this instance into its . /// public static implicit operator Action(DisposableAction da) => da.Action; /// /// The of this functor. /// public Action Action { get; } /// public void Dispose() => _onDispose(); readonly Action _onDispose; } }