Settings for the last or next wait operation on an automation control
        /// <summary>
        /// Executes a wait on the IsVisible state of a WPF automation control.
        /// </summary>
        /// <param name="waitSettings">The wait settings.</param>
        /// <param name="throwExceptionOnTimeout">if set to <c>true</c> [throw exception on timeout].</param>
        /// <param name="stateToWaitFor">The visibility state to wait for</param>
        /// <returns>
        /// The control owning the wait settings
        /// </returns>
        /// <exception cref="TimeoutException">Thrown if the timeout period is reached before the expected state is true</exception>
        private static AutomationControl ExecuteWpfVisiblityWait(WaitSettings waitSettings, bool throwExceptionOnTimeout, bool stateToWaitFor)
        {
            var control = waitSettings.WaitRoot;
            var element = control.AutomationElement;

            SpinWait.SpinUntil(() =>
            {
                return control.IsVisible == stateToWaitFor;
            }, waitSettings.ActualTimeOut);

            if(throwExceptionOnTimeout && control.IsVisible != stateToWaitFor)
            {
                throw new TimeoutException("Wait operation timed out before the required visibility was achieved");
            }

            return waitSettings.WaitRoot;
        }
        /// <summary>
        /// Executes a wait operation
        /// </summary>
        /// <param name="waitSettings">The wait settings.</param>
        /// <param name="throwExceptionOnTimeout">if set to <c>true</c> a TimeoutException will be thrown on timeout without success.</param>
        /// <returns>
        /// The control owning the wait settings
        /// </returns>
        /// <exception cref="TimeoutException">Thrown if the timeout period is reached before the expected state is true</exception>
        private static AutomationControl ExecuteWait(WaitSettings waitSettings, TimeSpan timeout, bool throwExceptionOnTimeout = false)
        {
            if(waitSettings.WaitType == WaitType.NotSet)
            {
                throw new WaitTypeNotSetException();
            }

            waitSettings.WaitTime = timeout;

            switch(waitSettings.WaitType)
            {
                case WaitType.Enabled:
                case WaitType.Disabled:
                    return ExecuteIsEnabledWait(waitSettings, throwExceptionOnTimeout);
                case WaitType.Visible:
                case WaitType.Hidden:
                    return ExecuteIsVisibleWait(waitSettings, throwExceptionOnTimeout);
                case WaitType.Exists:
                case WaitType.NotExists:
                    return ExecuteExistsWait(waitSettings, throwExceptionOnTimeout);
                default:
                    return waitSettings.WaitRoot;
            }
        }
        /// <summary>
        /// Executes a wait on the IsEnabled state of the target automation control.
        /// </summary>
        /// <param name="waitSettings">The wait settings.</param>
        /// <param name="throwExceptionOnTimeout">if set to <c>true</c> throw an exception if the timeout expires.</param>
        /// <returns>
        /// The control owning the wait settings
        /// </returns>
        /// <exception cref="TimeoutException">Thrown if the timeout period is reached before the expected state is true</exception>
        private static AutomationControl ExecuteIsEnabledWait(WaitSettings waitSettings, bool throwExceptionOnTimeout = false)
        {
            var expectedState = waitSettings.WaitType == WaitType.Enabled;

            if(waitSettings.WaitRoot.IsEnabled == expectedState)
            {
                return waitSettings.WaitRoot;
            }

            SpinWait.SpinUntil(() => waitSettings.WaitRoot.IsEnabled == expectedState, waitSettings.ActualTimeOut);
            if(throwExceptionOnTimeout && waitSettings.WaitRoot.IsEnabled != expectedState)
            {
                throw new TimeoutException("Wait operation timed out before the required enabled state was achieved");
            }

            return waitSettings.WaitRoot;
        }
        /// <summary>
        /// Executes a wait on the IsVisible state of the target automation control.
        /// </summary>
        /// <param name="waitSettings">The wait settings.</param>
        /// <param name="throwExceptionOnTimeout">if set to <c>true</c> throw an execption if the timeout expires.</param>
        /// <returns>
        /// The control owning the wait settings
        /// </returns>
        /// <exception cref="TimeoutException">Thrown if the timeout period is reached before the expected state is true</exception>
        private static AutomationControl ExecuteIsVisibleWait(WaitSettings waitSettings, bool throwExceptionOnTimeout)
        {
            var expectedState = waitSettings.WaitType == WaitType.Visible;

            if(waitSettings.WaitRoot.IsVisible == expectedState)
            {
                return waitSettings.WaitRoot;
            }

            if(waitSettings.WaitRoot.AutomationElement != null && waitSettings.WaitRoot.AutomationElement.Current.FrameworkId == "WPF")
            {
                return ExecuteWpfVisiblityWait(waitSettings, throwExceptionOnTimeout, expectedState);
            }

            return ExecuteWinFormsVisibilityWait(waitSettings, throwExceptionOnTimeout, expectedState);
        }
        /// <summary>
        /// Executes a wait on the existence of the target automation control.
        /// </summary>
        /// <param name="waitSettings">The wait settings.</param>
        /// <param name="throwExceptionOnTimeout">if set to <c>true</c> throw an exception if the timeout expires.</param>
        /// <returns>
        /// The control owning the wait settings
        /// </returns>
        /// <exception cref="TimeoutException">Thrown if the timeout period is reached before the expected state is true</exception>
        private static AutomationControl ExecuteExistsWait(WaitSettings waitSettings, bool throwExceptionOnTimeout)
        {
            var stateToWaitFor = waitSettings.WaitType == WaitType.NotExists;
            var control = waitSettings.WaitRoot;

            SpinWait.SpinUntil(() =>
            {
                control = control.FindSettings.AsDefault();
                return control.IsProxy == stateToWaitFor;
            }, waitSettings.ActualTimeOut);

            if(throwExceptionOnTimeout && control.IsProxy != stateToWaitFor)
            {
                throw new TimeoutException("Wait operation timed out before the required existence was achieved");
            }

            waitSettings.WaitRoot.AutomationElement = control.AutomationElement;
            return waitSettings.WaitRoot;
        }