public static TaskList Replay(GenericTodoEvent e, TaskList tasklist, Stack <UndoAction> undoStack, out bool saveEvent, out bool triggerRefresh)
        {
            saveEvent      = true;
            triggerRefresh = false;

            // Acquire the task from the global task list, to determine it's current state.
            Task task = tasklist.AllTaskReader(e.Id);

            try {
                // Handle cases where the task does not yet exist. If the event is a creation event, (or undo deletion event) then this is obviously expected.
                // If the event is an 'undo' of a creation event, then we can safely dispose of the event, since it would just erase the expected task anyway. We will still save it though, in case of an out-or-order arrival
                // If the event is a 'Task activated' or 'Task Started' or 'Task Edited' event, then we will create the event implicitly, since we can do so with no harm done.
                // If the event is a 'Task revived' event, then this is expected, but only if the 'original' task acutally existed and is a failed state. Otherwise, we will have to implicitly 'fail' the original one first.
                if (task == null)
                {
                    if (e.EventType == EventTypes.TaskAdded || e.EventType == EventTypes.ChildTaskAdded || e.EventType == EventTypes.TaskDeletedUndo || (e.EventType == EventTypes.TaskRevived && tasklist.FailedTaskReader(e.Original.Value) != null))
                    {
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                    else if (e.EventType == EventTypes.TaskRevived && tasklist.ActiveTaskReader(e.Original.Value) != null && tasklist.ActiveTaskReader(e.Original.Value).Category != CategoryVals.Deferred)
                    {
                        // Implicitly 'fail' the original task first
                        tasklist.FailTask(tasklist.AllTaskReader(e.Original.Value), e.Timestamp);
                        undoStack.Clear();
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                    else if (e.EventType == EventTypes.TaskAddedUndo || e.EventType == EventTypes.ChildTaskAddedUndo || e.EventType == EventTypes.TaskEdited || e.EventType == EventTypes.TaskDeleted)
                    {
                        // Save the event, but do not actually do anything. We will still save this event incase a subsequent request 'fills in the gap' so to speak (I.e. out of order arrival).
                        saveEvent      = true;
                        triggerRefresh = false;
                        return(tasklist);
                    }
                    else if (e.EventType == EventTypes.TaskActivated || e.EventType == EventTypes.TaskStarted)
                    {
                        // If the task does not exist yet, we are happy to create it implicitly.
                        if (e.Parent.HasValue)
                        {
                            tasklist.CreateNewSubtask(e.Name, tasklist.AllTaskReader(e.Parent.Value), e.Category, e.Timestamp, e.Id);
                            undoStack.Push(new UndoAction(EventTypes.ChildTaskAdded, e.Id));
                        }
                        else
                        {
                            tasklist.CreateNewIndependentTask(e.Name, e.Category, e.Timestamp, e.ColourId, e.Id);
                            undoStack.Push(new UndoAction(EventTypes.TaskAdded, e.Id));
                        }
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                    else if (e.EventType == EventTypes.TaskCompleted && e.Category != CategoryVals.Deferred)
                    {
                        // Create the task implicitly, then apply the 'linking' event, and then perform the taskCompleted handler itself. This is the only case of a "double link".
                        if (e.Parent.HasValue)
                        {
                            tasklist.CreateNewSubtask(e.Name, tasklist.AllTaskReader(e.Parent.Value), e.Category, e.Timestamp, e.Id);
                            undoStack.Push(new UndoAction(EventTypes.ChildTaskAddedUndo, e.Id));
                        }
                        else
                        {
                            tasklist.CreateNewIndependentTask(e.Name, e.Category, e.Timestamp, e.ColourId, e.Id);
                            undoStack.Push(new UndoAction(EventTypes.TaskAdded, e.Id));
                        }
                        Handlers[EventTypes.TaskStarted](e, tasklist, undoStack);
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                    else
                    {
                        throw new InvalidOperationException("Illegal event applied to a task id with no-corresponding-existing task. Eventtype: " + e.EventType);
                    }
                }
                // Handle cases where the task is deferred.
                // If the event is a creation event, we can safely dispose of the event without saving it, because the task has already progressed to a state beyond the state that those incoming events cause.
                // If the event is an undo-activation event, or undo-deletion event, we can skip execution of the event, but it must still be saved in order to handle out-of-order arrival; for example an activation event could 'fill the gap'.
                // If the event is a 'start task' or 'fail task', we are happy to fill in the implicit activation action.
                else if (task.Category == CategoryVals.Deferred)
                {
                    if (e.EventType == EventTypes.TaskAdded || e.EventType == EventTypes.ChildTaskAdded)
                    {
                        // Throw this event away, it is harmless and uneeded in this case.
                        saveEvent      = false;
                        triggerRefresh = false;
                        return(tasklist);
                    }
                    else if (e.EventType == EventTypes.TaskActivatedUndo || e.EventType == EventTypes.TaskDeletedUndo)
                    {
                        // Save this event, but do not execute the replay of this event, since it would have no effect. We still save it to handle out of order arrival.
                        saveEvent      = true;
                        triggerRefresh = false;
                        return(tasklist);
                    }
                    else if (e.EventType == EventTypes.TaskStarted || e.EventType == EventTypes.TaskFailed)
                    {
                        tasklist.ActivateTask(task, e.Category, e.Timestamp);
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                    else
                    {
                        return(Handlers[e.EventType](e, tasklist, undoStack));
                    }
                }
                // Handle the cases where the state is denoted by progress status of the task (the only remaining option)
                else
                {
                    if (IncomingEventsWhichTriggerRefreshForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType))
                    {
                        triggerRefresh = true;
                    }
                    if (IncomingEventsToIgnoreForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType))
                    {
                        saveEvent = false;
                        return(tasklist);
                    }
                    if (IncomingEventsToSaveButNotExecuteForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType))
                    {
                        saveEvent = true;
                        return(tasklist);
                    }
                    DoImplicitLinkingEventIfRequired(task, e, tasklist, undoStack);

                    return(Handlers[e.EventType](e, tasklist, undoStack));
                }
            }
            catch (InvalidOperationException exception) {
                // Add additional loggable information about the error that was thrown.
                throw new InvalidOperationException(exception.Message + ". When this error was thrown, the task was in state: " +
                                                    ((task == null) ? "NON-EXISTENT (NULL)" : (task.Category == CategoryVals.Deferred) ? "Deferred" : ProgressStatusVals.getString(task.ProgressStatus)),
                                                    exception);
            }
        }