/// <summary>
        /// Creates a <see cref="WuApiController"/> which uses the given Interfaces to search, download and install updates.
        /// </summary>
        /// <param name="session">Session to be used.</param>
        /// <param name="updateCollectionFactory">Factory to create <see cref="IUpdateCollection"/>s.</param>
        /// <param name="systemInfo">System informations about the OS enviroment.</param>
        public WuApiController(IUpdateSession session, UpdateCollectionFactory updateCollectionFactory, ISystemInfo systemInfo)
        {
            if (session == null)
            {
                throw new ArgumentNullException(nameof(session));
            }
            if (updateCollectionFactory == null)
            {
                throw new ArgumentNullException(nameof(updateCollectionFactory));
            }
            if (systemInfo == null)
            {
                throw new ArgumentNullException(nameof(systemInfo));
            }

            UpdateSession = session;
            UpdateSession.ClientApplicationID = this.GetType().FullName;
            UpdateSearcher          = session.CreateUpdateSearcher();
            UpdateDownloader        = session.CreateUpdateDownloader();
            UpdateInstaller         = session.CreateUpdateInstaller();
            UpdateCollectionFactory = updateCollectionFactory;
            SystemInfo       = systemInfo;
            StateTransitions = SetupStateTransitions();

            EnterState((SystemInfo.IsRebootRequired()) ? (WuProcessState) new WuStateRebootRequired() : new WuStateReady());
            Log.Info("Initial state: " + _currentState.GetType().Name);
            if (Log.IsDebugEnabled)
            {
                OnStateChanged            += (s, e) => { Log.Debug($"Event {nameof(OnStateChanged)} fired: {e.ToString()}"); };
                OnAsyncOperationCompleted += (s, e) => { Log.Debug($"Event {nameof(OnAsyncOperationCompleted)} fired: {e.ToString()}"); };
                OnProgressChanged         += (s, e) => { Log.Debug($"Event {nameof(OnProgressChanged)} fired: {e.ToString()}"); };
            }
        }
        /// <summary>
        /// Leaves and disposes the current state and enters a new state.
        /// </summary>
        /// <param name="next">The new state to change to.</param>
        private void EnterState(WuProcessState next)
        {
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }
            using (ll.Lock(StateChangingLock))
            {
                if (_currentState != null)
                {
                    ThrowIfInvalidStateTransition(next.GetType());
                }
                Log.Info($"Changing state from '{_currentState?.DisplayName}' to '{next.DisplayName}'.");
                WuProcessState oldState = _currentState;
                try
                {
                    next.EnterState(oldState);
                }
                catch (Exception e)
                {
                    Log.Error($"Failed to enter state '{next?.DisplayName}' properly.", e);
                    Debug.Assert(_currentState != next, "Should not switch to the next state when an error occured.");
                    throw;
                }

                _currentState   = next;
                CurrentProgress = null;

                try
                {
                    oldState?.LeaveState();
                }
                catch (Exception e)
                {
                    Debug.Assert(true, $"Failed to leave state '{oldState?.DisplayName}' properly: {e.Message}."); // do not hide this exception in test scenarios
                    Log.Error($"Failed to leave state '{oldState?.DisplayName}' properly.", e);
                }
                finally
                {
                    (oldState as IDisposable)?.Dispose();
                }

                OnStateChanged?.Invoke(this, new StateChangedEventArgs((oldState != null) ? oldState.StateId : WuStateId.Ready, next.StateId));
                if (OnAsyncOperationCompleted != null && oldState is WuStateAsyncJob)
                {
                    if (oldState is WuStateSearching)
                    {
                        OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Searching, next.StateId));
                    }
                    else if (oldState is WuStateDownloading)
                    {
                        OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Downloading, next.StateId));
                    }
                    else if (oldState is WuStateInstalling)
                    {
                        OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Installing, next.StateId));
                    }
                    else
                    {
                        throw new NotImplementedException($"For {oldState.GetType()} are no {nameof(OnAsyncOperationCompleted)} event args implemented.");
                    }
                }
                Debug.Assert(_currentState == next, "State was not changed.");
                Debug.Assert(CurrentProgress == null, "Should reset progress when state changes.");
            }
        }