internal StepResult(bool success, NextStep next, FormPrompt feedback, FormPrompt prompt)
 {
     this.Success  = success;
     this.Next     = next;
     this.Feedback = feedback;
     this.Prompt   = prompt;
 }
        /// <summary>
        /// Generate a list of hero cards from a prompt definition.
        /// </summary>
        /// <param name="prompt">Prompt definition.</param>
        /// <returns>List of hero cards.</returns>
        public static IList <Attachment> GenerateHeroCards(this FormPrompt prompt)
        {
            var attachments = new List <Attachment>();
            var description = prompt.Description;

            foreach (var button in prompt.Buttons)
            {
                string image = button.Image ?? description.Image;
                attachments.Add(new HeroCard(
                                    title: button.Title ?? description.Title ?? string.Empty,
                                    subtitle: button.SubTitle ?? description.SubTitle,
                                    text: prompt.Prompt,
                                    images: (image == null ? null : (new List <CardImage>()
                {
                    new CardImage()
                    {
                        Url = image
                    }
                })),
                                    buttons: new List <CardAction>()
                {
                    new CardAction(ActionTypes.ImBack, button.Description, null, button.Message ?? button.Description, value: button.Message ?? button.Description)
                })
                                .ToAttachment());
            }
            return(attachments);
        }
        /// <summary>
        /// Generate a hero card from a FormPrompt.
        /// </summary>
        /// <param name="prompt">Prompt definition.</param>
        /// <returns>Either an empty list if no buttons or a list with one hero card.</returns>
        public static IList <Attachment> GenerateHeroCard(this FormPrompt prompt)
        {
            var actions = new List <CardAction>();

            foreach (var button in prompt.Buttons)
            {
                actions.Add(new CardAction(ActionTypes.ImBack, button.Description, button.Image, button.Message ?? button.Description, value: button.Message ?? button.Description));
            }

            var attachments = new List <Attachment>();

            if (actions.Count > 0)
            {
                var description = prompt.Description;
                // Facebook requires a title https://github.com/Microsoft/BotBuilder/issues/1678
                attachments.Add(new HeroCard(text: prompt.Prompt, title: description.Title ?? string.Empty, subtitle: description.SubTitle,
                                             buttons: actions,
                                             images: prompt.Description?.Image == null ? null : new List <CardImage>()
                {
                    new CardImage()
                    {
                        Url = description.Image
                    }
                })
                                .ToAttachment());
            }
            return(attachments);
        }
        /// <summary>
        /// Deep clone the FormPrompt.
        /// </summary>
        /// <returns> A deep cloned instance of FormPrompt.</returns>
        public object Clone()
        {
            var newPrompt = new FormPrompt();

            newPrompt.Prompt      = this.Prompt;
            newPrompt.Description = this.Description;
            newPrompt.Buttons     = new List <DescribeAttribute>(this.Buttons);
            newPrompt.Style       = this.Style;
            return(newPrompt);
        }
        private FormPrompt ClarifyPrompt(FieldStepState stepState, IRecognize <T> recognizer, T state)
        {
            var        clarify = NeedsClarification(stepState);
            FormPrompt prompt  = null;

            if (clarify != null)
            {
                var field    = ClarifyField(clarify, recognizer);
                var prompter = new Prompter <T>(field.Template(TemplateUsage.Clarify), field.Form, new RecognizeEnumeration <T>(field));
                prompt = prompter.Prompt(state, field, clarify.Response);
            }
            return(prompt);
        }
        public FormPrompt NotUnderstood(DialogContext context, T state, FormState form, IMessageActivity input)
        {
            var inputText = MessageActivityHelper.GetSanitizedTextInput(input);

            FormPrompt feedback   = null;
            var        iprompt    = _field.Prompt;
            var        fieldState = (FieldStepState)form.StepState;

            if (fieldState.State == FieldStepStates.SentPrompt)
            {
                feedback = Template(TemplateUsage.NotUnderstood).Prompt(state, _field, inputText);
            }
            else if (fieldState.State == FieldStepStates.SentClarify)
            {
                feedback = Template(TemplateUsage.NotUnderstood, ClarifyRecognizer(fieldState, _field.Prompt.Recognizer)).Prompt(state, _field, inputText);
            }
            return(feedback);
        }
        public async Task <StepResult> ProcessAsync(DialogContext context, T state, FormState form, IMessageActivity input, IEnumerable <TermMatch> matches)
        {
            var inputText = MessageActivityHelper.GetSanitizedTextInput(input);

            ValidateResult feedback = new ValidateResult();

            feedback.IsValid      = true;
            feedback.Feedback     = null;
            feedback.FeedbackCard = null;
            feedback.Choices      = null;
            FormPrompt prompt         = null;
            FormPrompt feedbackPrompt = null;
            var        iprompt        = _field.Prompt;
            var        fieldState     = (FieldStepState)form.StepState;
            object     response       = null;

            if (fieldState.State == FieldStepStates.SentPrompt)
            {
                // Response to prompt
                var firstMatch = matches.FirstOrDefault();
                if (matches.Count() == 1)
                {
                    response = firstMatch.Value;
                    if (_field.AllowsMultiple && response != null &&
                        (response.GetType() == typeof(string) || !response.GetType().IsIEnumerable()))
                    {
                        response = new List <object>()
                        {
                            response
                        };
                    }
                    feedback = await SetValueAsync(state, response, form);

                    if (!feedback.IsValid && feedback.Choices != null)
                    {
                        var choices = new Ambiguous(inputText.Substring(firstMatch.Start, firstMatch.Length), feedback.Choices);
                        fieldState.State          = FieldStepStates.SentClarify;
                        fieldState.Settled        = new List <object>();
                        fieldState.Clarifications = new List <Ambiguous>()
                        {
                            choices
                        };
                        response = SetValue(state, null);
                        prompt   = ClarifyPrompt((FieldStepState)form.StepState, iprompt.Recognizer, state);
                    }
                }
                else if (matches.Count() > 1)
                {
                    // Check multiple matches for ambiguity
                    var groups = MatchAnalyzer.GroupedMatches(matches);
                    // 1) Could be multiple match groups like for ingredients.
                    // 2) Could be overlapping matches like "onion".
                    // 3) Could be multiple matches where only one is expected.

                    if (!_field.AllowsMultiple)
                    {
                        // Create a single group of all possibilities if only want one value
                        var mergedGroup = groups.SelectMany((group) => group).ToList();
                        groups = new List <List <TermMatch> >()
                        {
                            mergedGroup
                        };
                    }
                    var ambiguous = new List <Ambiguous>();
                    var settled   = new List <object>();
                    foreach (var choices in groups)
                    {
                        if (choices.Count > 1)
                        {
                            var unclearResponses = string.Join(" ", (from choice in choices select inputText.Substring(choice.Start, choice.Length)).Distinct());
                            var values           = from match in choices select match.Value;
                            ambiguous.Add(new Ambiguous(unclearResponses, values));
                        }
                        else
                        {
                            var matchValue = choices.First().Value;
                            if (matchValue != null && matchValue.GetType() != typeof(string) && matchValue.GetType().IsIEnumerable())
                            {
                                foreach (var value in (System.Collections.IEnumerable)matchValue)
                                {
                                    settled.Add(value);
                                }
                            }
                            else
                            {
                                settled.Add(choices.First().Value);
                            }
                        }
                    }
                    if (settled.Count > 1)
                    {
                        // Remove no preference if present
                        settled.Remove(null);
                    }

                    if (ambiguous.Count > 0)
                    {
                        // Need 1 or more clarifications
                        fieldState.State          = FieldStepStates.SentClarify;
                        fieldState.Settled        = settled;
                        fieldState.Clarifications = ambiguous;
                        response = SetValue(state, null);
                        prompt   = ClarifyPrompt((FieldStepState)form.StepState, iprompt.Recognizer, state);
                    }
                    else
                    {
                        if (_field.AllowsMultiple)
                        {
                            response = settled;
                            feedback = await SetValueAsync(state, response, form);
                        }
                        else
                        {
                            Debug.Assert(settled.Count == 1);
                            response = settled.First();
                            feedback = await SetValueAsync(state, response, form);
                        }
                    }
                }
                var unmatched      = MatchAnalyzer.Unmatched(inputText, matches);
                var unmatchedWords = string.Join(" ", unmatched);
                var nonNoise       = Language.NonNoiseWords(Language.WordBreak(unmatchedWords)).ToArray();
                fieldState.Unmatched = null;
                if (_field.Prompt.Annotation.Feedback == FeedbackOptions.Always)
                {
                    fieldState.Unmatched = string.Join(" ", nonNoise);
                }
                else if (_field.Prompt.Annotation.Feedback == FeedbackOptions.Auto &&
                         nonNoise.Any() &&
                         unmatched.Any())
                {
                    fieldState.Unmatched = string.Join(" ", nonNoise);
                }
            }
            else if (fieldState.State == FieldStepStates.SentClarify)
            {
                if (matches.Count() == 1)
                {
                    // Clarified ambiguity
                    var clarify = NeedsClarification(fieldState);
                    fieldState.Settled.Add(matches.First().Value);
                    fieldState.Clarifications.Remove(clarify);
                    if (prompt == null)
                    {
                        // No clarification left, so set the field
                        if (_field.AllowsMultiple)
                        {
                            response = fieldState.Settled;
                            feedback = await SetValueAsync(state, response, form);
                        }
                        else
                        {
                            Debug.Assert(fieldState.Settled.Count == 1);
                            response = fieldState.Settled.First();
                            feedback = await SetValueAsync(state, response, form);
                        }
                        form.SetPhase(StepPhase.Completed);
                    }
                }
            }
            if (form.Phase() == StepPhase.Completed)
            {
                form.StepState = null;
                if (fieldState.Unmatched != null)
                {
                    if (feedback.FeedbackCard != null)
                    {
                        feedbackPrompt = feedback.FeedbackCard;
                    }
                    else if (feedback.Feedback != null)
                    {
                        feedbackPrompt = new FormPrompt {
                            Prompt = feedback.Feedback
                        };
                    }
                    else
                    {
                        if (fieldState.Unmatched != "")
                        {
                            feedbackPrompt = new Prompter <T>(_field.Template(TemplateUsage.Feedback), _field.Form, null).Prompt(state, _field, fieldState.Unmatched);
                        }
                        else
                        {
                            feedbackPrompt = new Prompter <T>(_field.Template(TemplateUsage.Feedback), _field.Form, null).Prompt(state, _field);
                        }
                    }
                }
            }
            var next = _field.Next(response, state);

            return(new StepResult(feedback.IsValid, next, feedbackPrompt ?? (feedback.FeedbackCard ?? new FormPrompt {
                Prompt = feedback.Feedback
            }), prompt));
        }
        /// <summary>
        /// Given a prompt definition generate messages to send back.
        /// </summary>
        /// <param name="prompt">Prompt definition.</param>
        /// <param name="preamble">Simple text message with all except last line of prompt to allow markdown in prompts.</param>
        /// <param name="promptMessage">Message with prompt definition including cards.</param>
        /// <returns>True if preamble should be sent.</returns>
        public static bool GenerateMessages(this FormPrompt prompt, IMessageActivity preamble, IMessageActivity promptMessage)
        {
            var  promptCopy  = (FormPrompt)prompt.Clone();
            bool hasPreamble = false;

            if (promptCopy.Buttons?.Count > 0 || promptCopy.Description?.Image != null)
            {
                // If we are generating cards we do not support markdown so create a separate message
                // for all lines except the last one.
                var lines = promptCopy.Prompt.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                if (lines.Length > 1)
                {
                    var builder = new StringBuilder();
                    for (var i = 0; i < lines.Length - 1; ++i)
                    {
                        if (i > 0)
                        {
                            builder.AppendLine();
                        }
                        builder.Append(lines[i]);
                    }
                    preamble.Text     = builder.ToString();
                    promptCopy.Prompt = lines.Last();
                    hasPreamble       = true;
                }
                if (promptCopy.Buttons?.Count > 0)
                {
                    var style = promptCopy.Style;
                    if (style == ChoiceStyleOptions.Auto)
                    {
                        foreach (var button in promptCopy.Buttons)
                        {
                            // Images require carousel
                            if (button.Image != null)
                            {
                                style = ChoiceStyleOptions.Carousel;
                                break;
                            }
                        }
                    }
                    if (style == ChoiceStyleOptions.Carousel)
                    {
                        promptMessage.AttachmentLayout = AttachmentLayoutTypes.Carousel;
                        promptMessage.Attachments      = promptCopy.GenerateHeroCards();
                    }
                    else
                    {
                        promptMessage.AttachmentLayout = AttachmentLayoutTypes.List;
                        promptMessage.Attachments      = promptCopy.GenerateHeroCard();
                    }
                }
                else if (promptCopy.Description?.Image != null)
                {
                    promptMessage.AttachmentLayout = AttachmentLayoutTypes.List;
                    var card = new HeroCard()
                    {
                        Title = promptCopy.Prompt, Images = new List <CardImage> {
                            new CardImage(promptCopy.Description.Image)
                        }
                    };
                    promptMessage.Attachments = new List <Attachment> {
                        card.ToAttachment()
                    };
                }
            }
            else
            {
                promptMessage.Text = promptCopy.Prompt;
            }
            return(hasPreamble);
        }