internal StageChangedEventArgs(ulong programTime, Stage stage)
 {
     ProgramTime = programTime;
     Stage       = stage;
 }
 public bool TryGetStage(out Stage stage)
 {
     stage = Stage;
     return(!IsEndReached);
 }
        private void DoWork()
        {
            /* Check frozen. */
            if (FreezableClock.IsFrozen && !_pausedEvent.WaitOne())
            {
                return;
            }

            var now = ProgramTime;

            /* Do sleep waiting while greater than 2 x sleep period */
            var sleepWaitPeriod = SleepWaitPeriod;

            if (sleepWaitPeriod.Ticks > 0)
            {
                while (!_stageSkipped.Value && FreezableClock.Unit.ConvertTo((long)(_nextUpdateTime - now), TimeUnit.Tick) > sleepWaitPeriod.Ticks * 2)
                {
                    Thread.Sleep(sleepWaitPeriod);
                    now = ProgramTime;
                }
            }

            /* Do busy waiting while less than 2 x sleep period */
            while (now < _nextUpdateTime && !_stageSkipped.Value)
            {
                if (FreezableClock.IsFrozen)
                {
                    return;                          /* Exit busy waiting while paused */
                }
                now = ProgramTime;
            }

            /* Actual processing procedures */
            do
            {
                /* Reset skip state */
                _stageSkipped.Reset();

                /* Get next stage, exit on null (END REACHED) */
                Stage stage = null;
                try
                {
                    while (true) /* Loop only for skip */
                    {
                        stage = _stageProvider.Next();
                        now   = ProgramTime;
                        var eventArgs = new StageChangedEventArgs(now, stage);
                        StageChanged?.Invoke(this, eventArgs);
                        if (stage == null)
                        {
                            eventArgs.Action = StageAction.Terminate;
                        }
                        switch (eventArgs.Action)
                        {
                        case StageAction.Terminate:     /* Terminate entire program */
                            goto StopProgram;

                        case StageAction.Skip:     /* Skip current stage directly */
                            continue;

                        case StageAction.Pause:     /* Pause at the start of the stage */
                            Pause();
                            goto BreakOuterLoop;

                        default:     /* Accepted */
                            goto BreakOuterLoop;
                        }

BreakOuterLoop:
                        break;
                    }
                }
                catch (ThreadAbortException) { throw; } /* Aborted by stopping underlying runner */
                catch (Exception e) /* Process unhandled exception */
                {
                    var eventArgs = new ExceptionEventArgs(e);
                    UnhandledException?.Invoke(this, eventArgs);
                    if (!eventArgs.Handled)
                    {
                        throw new Exception("Unhandled exception", e);
                    }
                }

                /* Update time */
                if (stage != null)
                {
                    _nextUpdateTime = now + stage.Duration;
                }
                continue;

                /* Stop */
StopProgram:
                Stop();
                return;
            } while (now >= _nextUpdateTime || _stageSkipped.Value); /* Check if need to move next */
        }