/// <summary>
        /// Take the entities provided by LUIS (Setting and Value) to try and identify the vehicle setting we need to process.
        /// </summary>
        /// <param name="state">State object.</param>
        /// <param name="declarative">Indicates if special process for declarative utterances should be performed.</param>
        public void PostProcessSettingName(AutomotiveSkillState state, bool declarative = false)
        {
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            IList <SettingMatch> setting_matches             = new List <SettingMatch>();
            var           has_matching_value_for_any_setting = false;
            ISet <string> setting_names_to_remove            = new HashSet <string>();

            // The Setting entity will contain any identified vehicle setting that was present in the utterance, e.g. front right airflow
            // The Value entity will contain any identified value relating to a vehicle setting that was present in the utterance, e.g. warm
            IList <AvailableSetting> selected_settings = new List <AvailableSetting>();

            if (state.Entities.ContainsKey("SETTING"))
            {
                // If we have a Setting then try to find a match between the setting name provided and the available settings
                selected_settings = this.settingMatcher.MatchSettingNamesExactly(state.Entities["SETTING"].First());

                // If we have not found an exact setting match but we have a value then combine Setting and Value together to identify a match
                if (!selected_settings.Any() && state.Entities.ContainsKey("VALUE"))
                {
                    /* First try SETTING + VALUE entities combined to catch cases like "warm my seat",
                     * where the value can help disambiguate which setting the user meant.*/

                    List <string> entityValuesToMatch = new List <string>();
                    entityValuesToMatch.AddRange(state.Entities["SETTING"]);
                    entityValuesToMatch.AddRange(state.Entities["VALUE"]);

                    selected_settings = this.settingMatcher.MatchSettingNames(
                        entityValuesToMatch, SettingNameScoreThreshold,
                        SettingNameAntonymDisambPercentageOfMax, false);
                }

                // If we still haven't found a match then try to match with just the setting but not exactly this time
                if (!selected_settings.Any())
                {
                    List <string> entityValuesToMatch = new List <string>();
                    entityValuesToMatch.AddRange(state.Entities["SETTING"]);

                    selected_settings = this.settingMatcher.MatchSettingNames(
                        entityValuesToMatch, SettingNameScoreThreshold,
                        SettingNameAntonymDisambPercentageOfMax, false);
                }
            }

            // Do we have a selected setting name?
            if (selected_settings.Any())
            {
                List <string> entityValuesToMatch = new List <string>();

                if (state.Entities.ContainsKey("VALUE"))
                {
                    entityValuesToMatch.AddRange(state.Entities["VALUE"]);
                }
                else if (state.Entities.ContainsKey("SETTING"))
                {
                    // Sometimes the setting name itself is also a value, e.g., "defog"
                    entityValuesToMatch.AddRange(state.Entities["SETTING"]);
                }

                foreach (var setting_info in selected_settings)
                {
                    IList <SelectableSettingValue> selected_values = new List <SelectableSettingValue>();

                    if (entityValuesToMatch.Any())
                    {
                        IList <SelectableSettingValue> selectable_values = new List <SelectableSettingValue>();
                        foreach (var value in setting_info.Values)
                        {
                            SelectableSettingValue selectable = new SelectableSettingValue
                            {
                                CanonicalSettingName = setting_info.CanonicalName,
                                Value = value
                            };
                            selectable_values.Add(selectable);
                        }

                        /* From the available setting values for the given setting name identify which one applies for this setting name
                         * e.g. Set (when users says set temperature to 21 degrees
                         * e.g. Increase (when user says increase temperature)
                         * e.g. Decrease (when user says decrease temperature)
                         * e.g. Off, Alert, Alert and Brake when user wants to control Park Assist
                         */

                        selected_values = this.settingMatcher.DisambiguateSettingValues(
                            entityValuesToMatch, selectable_values,
                            SettingValueAntonymDisambThreshold, SettingValueAntonymDisambPercentageOfMax);

                        // If we don't even have a VALUE entity, we can't match multiple values.
                        // If the SETTING entity is really also a value, then it must match only one value.
                        if (!state.Entities.ContainsKey("VALUE") && selected_values.Count() > 1)
                        {
                            selected_values.Clear();
                        }

                        // For all selected values we return the canonical name for both the name and value
                        foreach (var selected_value in selected_values)
                        {
                            SettingMatch match = new SettingMatch
                            {
                                SettingName = setting_info.CanonicalName,
                                Value       = selected_value.Value.CanonicalName
                            };
                            setting_matches.Add(match);
                            has_matching_value_for_any_setting = true;
                        }
                    }

                    if (!selected_values.Any())
                    {
                        SettingMatch match = new SettingMatch
                        {
                            SettingName = setting_info.CanonicalName
                        };
                        setting_matches.Add(match);
                    }

                    AddAll(setting_names_to_remove, setting_info.IncludedSettings);
                }
            }
            else if (state.Entities.ContainsKey("VALUE") && !state.Entities.ContainsKey("SETTING"))
            {
                /*  If we have no SETTING entity, match the VALUE entities against all the values of all the settings.
                 *  This handles queries like "make it warmer" or "defog", where the value implies the setting.*/
                List <string> entityValuesToMatch = new List <string>();
                entityValuesToMatch.AddRange(state.Entities["VALUE"]);

                setting_matches = this.settingMatcher.MatchSettingValues(
                    entityValuesToMatch, SettingValueScoreThreshold,
                    SettingValueAntonymDisambPercentageOfMax);

                has_matching_value_for_any_setting = true;

                foreach (var match in setting_matches)
                {
                    var setting_info = this.settingList.FindSetting(match.SettingName);
                    if (setting_info != null)
                    {
                        AddAll(setting_names_to_remove, setting_info.IncludedSettings);
                    }
                }
            }

            // If at least one setting has a matching value, remove all settings with no matching value.
            // This effectively disambiguates the settings by their available values.
            // Also remove 'included' settings.
            IList <SettingMatch> new_setting_matches = new List <SettingMatch>();

            foreach (var match in setting_matches)
            {
                if ((!has_matching_value_for_any_setting || !string.IsNullOrEmpty(match.Value)) &&
                    !setting_names_to_remove.Contains(match.SettingName))
                {
                    new_setting_matches.Add(match);
                }
            }

            setting_matches = new_setting_matches;

            var(opt_amount, isRelative) = OptionalAmount(state, false);

            foreach (var setting_match in setting_matches)
            {
                SettingChange setting_change = new SettingChange
                {
                    SettingName = setting_match.SettingName
                };

                var value_info = this.settingList.FindSettingValue(setting_match.SettingName, setting_match.Value);
                if (declarative)
                {
                    // If the user makes a declarative statement, it means that they're unhappy with the status quo.
                    // So, we use the antonym of the value to get the opposite of the thing they're unhappy with,
                    // which should hopefully make them happy.
                    // If there is no antonym listed, then we want to return an empty value because we were unable to find
                    // the correct value.
                    if (value_info != null)
                    {
                        setting_change.Value = value_info.Antonym;
                    }
                }
                else
                {
                    setting_change.Value = setting_match.Value;
                }

                if (opt_amount != null && value_info != null && value_info.ChangesSignOfAmount)
                {
                    (opt_amount, isRelative) = OptionalAmount(state, true);
                }

                setting_change.Amount           = opt_amount;
                setting_change.IsRelativeAmount = isRelative;

                state.Changes.Add(setting_change);
            }

            if (!setting_matches.Any() && opt_amount != null)
            {
                SettingChange setting_change = new SettingChange
                {
                    Amount           = opt_amount,
                    IsRelativeAmount = isRelative
                };
                state.Changes.Add(setting_change);
            }
        }
        /// <summary>
        /// Apply the user's selection to the setting values.
        /// </summary>
        /// <param name="state">State object.</param>
        /// <param name="entityValues">List of entity values.</param>
        /// <returns>The selected SettingChanges or null if no selection was made.</returns>
        public IList <SettingChange> ApplySelectionToSettingValues(AutomotiveSkillState state, List <string> entityValues)
        {
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            if (entityValues == null)
            {
                throw new ArgumentNullException(nameof(entityValues));
            }

            var settingValues = state.GetUniqueSettingValues();

            ISet <string> selectedSettingValues = new HashSet <string>();

            if (entityValues.Any() && settingValues.Any())
            {
                IList <SelectableSettingValue> selectableSettingValues = new List <SelectableSettingValue>();
                foreach (var change in state.Changes)
                {
                    SelectableSettingValue selectable = new SelectableSettingValue
                    {
                        CanonicalSettingName = change.SettingName
                    };
                    var availableValue = this.settingList.FindSettingValue(change.SettingName, change.Value);
                    if (availableValue != null)
                    {
                        selectable.Value = availableValue;
                    }
                    else
                    {
                        availableValue = new AvailableSettingValue
                        {
                            CanonicalName = change.Value
                        };
                        selectable.Value = availableValue;
                    }

                    selectableSettingValues.Add(selectable);
                }

                var selected_values = this.settingMatcher.DisambiguateSettingValues(
                    entityValues,
                    selectableSettingValues, SettingValueAntonymDisambThreshold, SettingValueAntonymDisambPercentageOfMax);

                foreach (var selected_value in selected_values)
                {
                    selectedSettingValues.Add(selected_value.Value.CanonicalName);
                }
            }

            IList <SettingChange> newCandidates = new List <SettingChange>();

            foreach (var candidate in state.Changes)
            {
                if (selectedSettingValues.Contains(candidate.Value))
                {
                    newCandidates.Add(candidate);
                }
            }

            if (!Util.IsNullOrEmpty(newCandidates))
            {
                return(newCandidates);
            }

            return(null);
        }