private static async Task <PromptResult> ReadAnswerFromStenoDeviceAsync(TermModel term)
        {
            string[]    strokesAsSteno          = term.Steno.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            var         strokes                 = new List <Stroke>();
            var         cancellationTokenSource = new CancellationTokenSource();
            IDisposable strokesReceived         = _stenoDevice.StrokeReceived.Subscribe(
                stroke =>
            {
                strokes.Add(stroke);
                if (strokes.Count == strokesAsSteno.Length)
                {
                    cancellationTokenSource.Cancel();
                }
            });

            _stenoDevice.StartReadingStrokes();

            try
            {
                while (!cancellationTokenSource.IsCancellationRequested)
                {
                    try
                    {
                        await Task.Delay(TimeSpan.FromSeconds(1), cancellationTokenSource.Token);
                    }
                    catch (TaskCanceledException)
                    {
                    }
                }
            }
            finally
            {
                strokesReceived.Dispose();

                _stenoDevice.StopReadingStrokes();
            }

            string steno = strokes.Count == 1 ? $"/{strokes[0].Steno}" : string.Join("/", strokes.Select(x => x.Steno));

            Console.WriteLine(steno);

            return(PromptResult.Success(steno));
        }
        private static Task <PromptResult> ReadAnswerFromConsoleAsync(TermModel term)
        {
            string line = Console.ReadLine();

            return(Task.FromResult(string.IsNullOrEmpty(line) ? PromptResult.Canceled() : PromptResult.Success(line)));
        }
        private static async Task QuizAsync(
            IEnumerable <LessonModel> lessons,
            string questionPrompt,
            string answerPrompt,
            Func <TermModel, string> questionDelegate,
            Func <TermModel, string> answerDelegate,
            Func <TermModel, Task <PromptResult> > promptDelegate,
            StringComparison comparisonType)
        {
            lessons = lessons.ToArray();

            TermModel[] terms               = lessons.SelectMany(x => x.Terms).ToArray();
            var         random              = new Random();
            var         correctAnswers      = 0;
            var         totalAnswerAttempts = 0;
            TermModel   term = terms[random.Next(0, terms.Length)];

            while (true)
            {
                Console.Clear();

                Console.WriteLine($"Lesson{(lessons.Count() != 1 ? "s" : "")}: {string.Join(", ", lessons.Select(x => x.Name))}");
                Console.WriteLine();
                if (totalAnswerAttempts > 0)
                {
                    Console.WriteLine($"{correctAnswers} / {totalAnswerAttempts} = {(correctAnswers / (decimal)totalAnswerAttempts * 100).ToString("#0.0")}%");
                    Console.WriteLine();
                }

                Console.WriteLine($"{questionPrompt}: {questionDelegate(term)}");
                Console.Write($"{answerPrompt}: ");

                PromptResult result = await promptDelegate(term);

                if (result.Type == PromptResultType.Canceled)
                {
                    Console.Clear();
                    return;
                }
                if (string.IsNullOrEmpty(result.Answer))
                {
                    continue;
                }

                bool correct = string.Equals(result.Answer, answerDelegate(term), comparisonType);

                correctAnswers += correct ? 1 : 0;
                totalAnswerAttempts++;

                Console.WriteLine();
                using (new ConsoleColorContext(correct ? ConsoleColor.Green : ConsoleColor.Red))
                {
                    Console.WriteLine(correct ? "Correct!" : "Incorrect");
                }

                Thread.Sleep(TimeSpan.FromSeconds(2));

                if (correct)
                {
                    term = terms[random.Next(0, terms.Length)];
                }
            }
        }