/// <summary>
        /// Gets last execution context. Don't override this
        /// </summary>
        /// <returns></returns>
        internal virtual WorkflowState GetLastExecContext()
        {
            var req = new DescribeWorkflowExecutionRequest
            {
                Domain    = _request.Domain,
                Execution = _decisionTask.WorkflowExecution
            };

            var res = _swfClient.DescribeWorkflowExecutionAsync(req).Result;

            return(WorkflowStateSerializer.Deserialize(res.WorkflowExecutionDetail.LatestExecutionContext));
        }
        /// <summary>
        /// Walks through the relevant part of the event history chain and populates the decision context. Then calls
        /// on the appropriate workflow decision object to make a decision.
        /// </summary>
        /// <returns>Decision on how to proceed.</returns>
        internal RespondDecisionTaskCompletedRequest Decide()
        {
            // Step 1: Walk through the relevant part of the event history chain and populate the decision context

            // Retrieve and store the workflow information
            _decisionContext.WorkflowName    = _decisionTask.WorkflowType.Name;
            _decisionContext.WorkflowVersion = _decisionTask.WorkflowType.Version;
            _decisionContext.WorkflowId      = _decisionTask.WorkflowExecution.WorkflowId;

            // TODO: Warning. Need to check if it's the same as historyEvent.DecisionTaskCompletedEventAttributes.ExecutionContext
            // Move call to an appropriate event
            _decisionContext.LastExecutionContext = GetLastExecContext();

            // Walk through the chain of events based on event ID to identify what we need to decide on
            Debug.WriteLine(">>> Workflow: " + _decisionContext.WorkflowName);

            Logger.Debug("Workflow: {0}, PreviousStartedEventId: {1}, StartedEventId: {2}",
                         _decisionContext.WorkflowName,
                         _decisionTask.PreviousStartedEventId,
                         _decisionTask.StartedEventId
                         );

            var logData = new StringBuilder();

            foreach (var historyEvent in Events)
            {
                logData.AppendLine($"Event Type: [{historyEvent.EventId}] [{historyEvent.EventType}]");

                ProcessDecisionContext(historyEvent, _decisionContext);
            }

            Logger.Info(logData.ToString());


            Logger.Info("CONTEXT Event Type: {0}, Timers: {1}, Fired: {2}, Canceled: {3}",
                        _decisionContext.DecisionType,
                        _decisionContext.Timers.Count,
                        _decisionContext.FiredTimers.Count,
                        _decisionContext.CanceledTimers.Count
                        );

            // Step 2: decide on what to do based on the processed events

            // Getting the correct instance of the decider.
            var decider = _workflow.GetDecider(_decisionContext.WorkflowId);
            //(IDecider) Activator.CreateInstance(_workflows[_decisionContext.WorkflowName]);

            var decisionCompletedRequest = MakeDecision(decider, _decisionContext);

            logData.Clear();
            bool needToClearStore = false;

            foreach (var d in decisionCompletedRequest.Decisions)
            {
                logData.AppendLine($"Decision Type: {d.DecisionType}");

                needToClearStore = (d.DecisionType == DecisionType.CompleteWorkflowExecution ||
                                    d.DecisionType == DecisionType.CancelWorkflowExecution ||
                                    d.DecisionType == DecisionType.FailWorkflowExecution);
            }

            if (needToClearStore)
            {
                ClearStoreData(_decisionContext);
            }

            Logger.Warn(logData.ToString());

            // Assign the task token and return.
            decisionCompletedRequest.TaskToken        = _decisionTask.TaskToken;
            decisionCompletedRequest.ExecutionContext = WorkflowStateSerializer.Serialize(_decisionContext.ExecutionContext);

            return(decisionCompletedRequest);
        }
        /// <summary>
        /// Fills the decision context (a god object contains all the particular data based on event type).
        /// </summary>
        /// <param name="historyEvent">Amazon history event. It's a core Amazon SWF object storing all the serialized workflow events. </param>
        internal void ProcessDecisionContext(HistoryEvent historyEvent, WorkflowDecisionContext decisionContext)
        {
            //impl
            if (historyEvent.EventType == EventType.WorkflowExecutionStarted)
            {
                decisionContext.DecisionType  = historyEvent.EventType;
                decisionContext.InputRef      = historyEvent.WorkflowExecutionStartedEventAttributes.Input;
                decisionContext.StartingInput = historyEvent.WorkflowExecutionStartedEventAttributes.Input;
                if (historyEvent.WorkflowExecutionStartedEventAttributes.ParentWorkflowExecution != null)
                {
                    decisionContext.WorkflowParentId =
                        historyEvent.WorkflowExecutionStartedEventAttributes.ParentWorkflowExecution.WorkflowId;
                }

                AddInput(decisionContext.InputRef);
            }
            //impl
            else if (historyEvent.EventType == EventType.WorkflowExecutionContinuedAsNew)
            {
                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.InputRef     = historyEvent.WorkflowExecutionContinuedAsNewEventAttributes.Input;
            }
            else if (historyEvent.EventType == EventType.WorkflowExecutionCancelRequested)
            {
                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.Cause        = historyEvent.WorkflowExecutionCancelRequestedEventAttributes.Cause;
            }
            else if (historyEvent.EventType == EventType.DecisionTaskCompleted)
            {
                // If a decision task completed event was encountered, use it to save
                // some of the key information as the execution context is not available as part of
                // the rest of the ActivityTask* event attributes.
                // NB: We don't act on this event.
                decisionContext.ScheduledEventId = historyEvent.DecisionTaskCompletedEventAttributes.ScheduledEventId;
                decisionContext.StartedEventId   = historyEvent.DecisionTaskCompletedEventAttributes.StartedEventId;
                decisionContext.ExecutionContext =
                    WorkflowStateSerializer.Deserialize(historyEvent.DecisionTaskCompletedEventAttributes.ExecutionContext);
            }
            else if (historyEvent.EventType == EventType.ActivityTaskScheduled)
            {
                // If an activity task scheduled event was encountered, use it to save
                // some of the key information as the activity information is not available as part of
                // the rest of the ActivityTask* event attributes. We don't act on this event.
                decisionContext.ActivityId      = historyEvent.ActivityTaskScheduledEventAttributes.ActivityId;
                decisionContext.ActivityName    = historyEvent.ActivityTaskScheduledEventAttributes.ActivityType.Name;
                decisionContext.ActivityVersion = historyEvent.ActivityTaskScheduledEventAttributes.ActivityType.Version;
                decisionContext.Control         = historyEvent.ActivityTaskScheduledEventAttributes.Control;
                decisionContext.InputRef        = historyEvent.ActivityTaskScheduledEventAttributes.Input;

                // Remove related timer if applicable.
                var state = SchedulableStateSerializer.Deserialize(decisionContext.Control);
                foreach (var timer in decisionContext.Timers)
                {
                    var timerState = SchedulableStateSerializer.Deserialize(timer.Value.Control);
                    if (timerState != null && timerState.Equals(state))
                    {
                        if (decisionContext.FiredTimers.ContainsKey(timer.Key))
                        {
                            decisionContext.FiredTimers.Remove(timer.Key);
                        }

                        decisionContext.Timers.Remove(timer.Key);
                        break;
                    }
                }
            }
            //impl
            else if (historyEvent.EventType == EventType.ActivityTaskCompleted)
            {
                decisionContext.DecisionType     = historyEvent.EventType;
                decisionContext.ScheduledEventId = historyEvent.ActivityTaskCompletedEventAttributes.ScheduledEventId;
                decisionContext.StartedEventId   = historyEvent.ActivityTaskCompletedEventAttributes.StartedEventId;
                decisionContext.ResultRef        = historyEvent.ActivityTaskCompletedEventAttributes.Result;

                var evt = FindEventTypeById(decisionContext.ScheduledEventId);
                decisionContext.ActivityId = evt.ActivityTaskScheduledEventAttributes.ActivityId;
                decisionContext.Control    = evt.ActivityTaskScheduledEventAttributes.Control;
                decisionContext.InputRef   = evt.ActivityTaskScheduledEventAttributes.Input;

                AddInput(decisionContext.InputRef);
                AddResult(decisionContext.Control, decisionContext.ResultRef);
            }
            //impl
            else if (historyEvent.EventType == EventType.ActivityTaskFailed)
            {
                decisionContext.DecisionType     = historyEvent.EventType;
                decisionContext.ScheduledEventId = historyEvent.ActivityTaskFailedEventAttributes.ScheduledEventId;
                decisionContext.StartedEventId   = historyEvent.ActivityTaskFailedEventAttributes.StartedEventId;
                decisionContext.Details          = historyEvent.ActivityTaskFailedEventAttributes.Details;
                decisionContext.Reason           = historyEvent.ActivityTaskFailedEventAttributes.Reason;

                var evt = FindEventTypeById(decisionContext.ScheduledEventId);
                decisionContext.ActivityId = evt.ActivityTaskScheduledEventAttributes.ActivityId;
                decisionContext.Control    = evt.ActivityTaskScheduledEventAttributes.Control;
                decisionContext.InputRef   = evt.ActivityTaskScheduledEventAttributes.Input;
            }
            //impl
            else if (historyEvent.EventType == EventType.ActivityTaskTimedOut)
            {
                decisionContext.DecisionType     = historyEvent.EventType;
                decisionContext.ScheduledEventId = historyEvent.ActivityTaskTimedOutEventAttributes.ScheduledEventId;
                decisionContext.StartedEventId   = historyEvent.ActivityTaskTimedOutEventAttributes.StartedEventId;
                decisionContext.Details          = historyEvent.ActivityTaskTimedOutEventAttributes.Details;
                decisionContext.TimeoutType      = historyEvent.ActivityTaskTimedOutEventAttributes.TimeoutType;

                var evt = FindEventTypeById(decisionContext.ScheduledEventId);
                decisionContext.ActivityId = evt.ActivityTaskScheduledEventAttributes.ActivityId;
                decisionContext.Control    = evt.ActivityTaskScheduledEventAttributes.Control;
                decisionContext.InputRef   = evt.ActivityTaskScheduledEventAttributes.Input;
            }
            else if (historyEvent.EventType == EventType.ScheduleActivityTaskFailed)
            {
                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.ActivityId   = historyEvent.ScheduleActivityTaskFailedEventAttributes.ActivityId;
                decisionContext.DecisionTaskCompletedEventId = historyEvent.ScheduleActivityTaskFailedEventAttributes.DecisionTaskCompletedEventId;
                decisionContext.ActivityName    = historyEvent.ScheduleActivityTaskFailedEventAttributes.ActivityType.Name;
                decisionContext.ActivityVersion = historyEvent.ScheduleActivityTaskFailedEventAttributes.ActivityType.Version;
                decisionContext.Cause           = historyEvent.ScheduleActivityTaskFailedEventAttributes.Cause;
            }
            else if (historyEvent.EventType == EventType.StartChildWorkflowExecutionInitiated)
            {
                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.InputRef     = historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes.Input;
                decisionContext.Control      = historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes.Control;

                //decisionContext.WorkflowId = historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes.WorkflowId;
                decisionContext.ChildWorkflowName =
                    historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion =
                    historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes.WorkflowType.Version;

                AddInput(decisionContext.InputRef);
            }
            else if (historyEvent.EventType == EventType.ChildWorkflowExecutionStarted)
            {
                decisionContext.DecisionType         = historyEvent.EventType;
                decisionContext.ChildWorkflowName    = historyEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion = historyEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowType.Version;
                //decisionContext.WorkflowId = historyEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowExecution.WorkflowId;
                decisionContext.WorkflowExecRunId = historyEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowExecution.RunId;
            }
            //impl
            else if (historyEvent.EventType == EventType.ChildWorkflowExecutionCompleted)
            {
                decisionContext.DecisionType         = historyEvent.EventType;
                decisionContext.ChildWorkflowName    = historyEvent.ChildWorkflowExecutionCompletedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion = historyEvent.ChildWorkflowExecutionCompletedEventAttributes.WorkflowType.Version;
                decisionContext.ResultRef            = historyEvent.ChildWorkflowExecutionCompletedEventAttributes.Result;

                var evt = FindEventTypeById(historyEvent.ChildWorkflowExecutionCompletedEventAttributes.InitiatedEventId);
                decisionContext.Control = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Control;

                AddInput(evt.StartChildWorkflowExecutionInitiatedEventAttributes.Input);
                AddResult(decisionContext.Control, decisionContext.ResultRef);
            }
            //impl
            else if (historyEvent.EventType == EventType.ChildWorkflowExecutionFailed)
            {
                decisionContext.DecisionType         = historyEvent.EventType;
                decisionContext.ChildWorkflowName    = historyEvent.ChildWorkflowExecutionFailedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion =
                    historyEvent.ChildWorkflowExecutionFailedEventAttributes.WorkflowType.Version;
                decisionContext.Details           = historyEvent.ChildWorkflowExecutionFailedEventAttributes.Details;
                decisionContext.Reason            = historyEvent.ChildWorkflowExecutionFailedEventAttributes.Reason;
                decisionContext.WorkflowExecRunId =
                    historyEvent.ChildWorkflowExecutionFailedEventAttributes.WorkflowExecution.RunId;

                var evt = FindEventTypeById(historyEvent.ChildWorkflowExecutionFailedEventAttributes.InitiatedEventId);
                decisionContext.Control  = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Control;
                decisionContext.InputRef = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Input;
            }
            else if (historyEvent.EventType == EventType.ChildWorkflowExecutionTerminated)
            {
                decisionContext.DecisionType         = historyEvent.EventType;
                decisionContext.ChildWorkflowName    = historyEvent.ChildWorkflowExecutionTerminatedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion =
                    historyEvent.ChildWorkflowExecutionTerminatedEventAttributes.WorkflowType.Version;
                decisionContext.Details           = "";
                decisionContext.WorkflowExecRunId =
                    historyEvent.ChildWorkflowExecutionTerminatedEventAttributes.WorkflowExecution.RunId;

                var evt = FindEventTypeById(historyEvent.ChildWorkflowExecutionTerminatedEventAttributes.InitiatedEventId);
                decisionContext.Control  = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Control;
                decisionContext.InputRef = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Input;
            }
            //impl
            else if (historyEvent.EventType == EventType.ChildWorkflowExecutionTimedOut)
            {
                decisionContext.DecisionType         = historyEvent.EventType;
                decisionContext.ChildWorkflowName    = historyEvent.ChildWorkflowExecutionTimedOutEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion =
                    historyEvent.ChildWorkflowExecutionTimedOutEventAttributes.WorkflowType.Version;
                decisionContext.TimeoutType       = historyEvent.ChildWorkflowExecutionTimedOutEventAttributes.TimeoutType;
                decisionContext.Details           = "";
                decisionContext.WorkflowExecRunId =
                    historyEvent.ChildWorkflowExecutionTimedOutEventAttributes.WorkflowExecution.RunId;

                var evt = FindEventTypeById(historyEvent.ChildWorkflowExecutionTimedOutEventAttributes.InitiatedEventId);
                decisionContext.Control  = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Control;
                decisionContext.InputRef = evt.StartChildWorkflowExecutionInitiatedEventAttributes.Input;
            }
            else if (historyEvent.EventType == EventType.MarkerRecorded)
            {
                // We don't act on markers but save the marker information in the decision context so that
                // the workflow has all the information it needs to make the decision. NOTE: values of markers
                // with the same names are overwritten.
                var markerName = historyEvent.MarkerRecordedEventAttributes.MarkerName;
                decisionContext.Markers[markerName] = Utils.GetDataFromStore(historyEvent.MarkerRecordedEventAttributes.Details, _storageClient);
            }
            else if (historyEvent.EventType == EventType.StartChildWorkflowExecutionFailed)
            {
                decisionContext.DecisionType      = historyEvent.EventType;
                decisionContext.ChildWorkflowName =
                    historyEvent.StartChildWorkflowExecutionFailedEventAttributes.WorkflowType.Name;
                decisionContext.ChildWorkflowVersion =
                    historyEvent.StartChildWorkflowExecutionFailedEventAttributes.WorkflowType.Version;
                decisionContext.Cause   = historyEvent.StartChildWorkflowExecutionFailedEventAttributes.Cause;
                decisionContext.Control = historyEvent.StartChildWorkflowExecutionFailedEventAttributes.Control;
            }
            else if (historyEvent.EventType == EventType.TimerStarted)
            {
                var timer = historyEvent.TimerStartedEventAttributes;

                decisionContext.DecisionType          = historyEvent.EventType;
                decisionContext.TimerId               = timer.TimerId;
                decisionContext.Timers[timer.TimerId] = timer;
            }
            else if (historyEvent.EventType == EventType.TimerFired)
            {
                var firedTimer = historyEvent.TimerFiredEventAttributes;

                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.TimerId      = firedTimer.TimerId;

                if (decisionContext.Timers.ContainsKey(firedTimer.TimerId))
                {
                    decisionContext.FiredTimers[firedTimer.TimerId] = firedTimer;
                    //decisionContext.Control = decisionContext.Timers[firedTimer.TimerId].Control;
                    //decisionContext.Timers.Remove(firedTimer.TimerId);
                }
            }
            else if (historyEvent.EventType == EventType.TimerCanceled)
            {
                var canceledTimer = historyEvent.TimerCanceledEventAttributes;

                decisionContext.DecisionType = historyEvent.EventType;
                decisionContext.TimerId      = canceledTimer.TimerId;

                if (decisionContext.Timers.ContainsKey(canceledTimer.TimerId))
                {
                    decisionContext.CanceledTimers[canceledTimer.TimerId] = canceledTimer;
                    //decisionContext.Control = decisionContext.Timers[canceledTimer.TimerId].Control;
                    //decisionContext.Timers.Remove(canceledTimer.TimerId);
                }
            }
            else if (historyEvent.EventType == EventType.StartChildWorkflowExecutionInitiated)
            {
            }
        }