/// <summary>
        /// Process an NLPMatchQuestionResponse and turn it into a MatchQuestionLogicResponse containing an answer,
        /// if any.
        /// </summary>
        /// <param name="matchQuestionModel">The NLP model to process.</param>
        /// <returns>A MatchQuestionLogicResponse containing either 1) nothing if there is no match or 2) the best match if there
        /// is a match.</returns>
        public static MatchQuestionLogicResponse ProcessNLPMatchQuestionsResponse(MatchQuestionModelResponse matchQuestionModel)
        {
            // Create a "no match" response
            MatchQuestionLogicResponse nullResponse = new MatchQuestionLogicResponse()
            {
                Msg_id = matchQuestionModel.msg_id
            };

            // Check to see whether there is at least a valid answer given
            if (matchQuestionModel == null ||
                !matchQuestionModel.IsComplete())
            {
                return(nullResponse);
            }

            // Collect all questions that might match
            List <MatchQuestionModelInfo> matchCandidates = new List <MatchQuestionModelInfo>();

            foreach (MatchQuestionModelInfo info in matchQuestionModel.possible_matches)
            {
                if (info.prob > MatchThreshold)
                {
                    matchCandidates.Add(info);
                }
            }

            // Call QueryHelperRecursive to get best match that also has an answer
            return(QueryHelperRecursive(matchCandidates, matchQuestionModel));
        }
        private void HandleResponse(KeyValuePair <WEBSOCKET_RESPONSE_TYPE, List <BaseModel> > model)
        {
            //TODO: what should the websocket do with bad responses?
            if (model.Key == WEBSOCKET_RESPONSE_TYPE.NONE)
            {
                return;
            }

            switch (model.Key)
            {
            case WEBSOCKET_RESPONSE_TYPE.MATCH_QUESTION:
                try
                {
                    {
                        MatchQuestionLogicResponse result = ProcessNLPResponse.ProcessNLPMatchQuestionsResponse(model.Value.Cast <MatchQuestionModelResponse>().ToList().First());

                        if (result != null)
                        {
                            if (ServerUtilities.msgIdToUserID[result.Msg_id] is NewQuestion)
                            {
                                if (result.Match)
                                {
                                    ChatbotWebSocketController.SendAnswerToQuestion(new ChatbotNewAnswerModel(result));
                                }
                                else
                                {
                                    var temp = ProcessChatbotLogic.GenerateModelCompareToOpenQuestions((NewQuestion)ServerUtilities.msgIdToUserID[result.Msg_id]);
                                    if (temp != null)
                                    {
                                        NLPWebSocketController.SendQuestionMatchRequest(temp);
                                    }
                                }
                            }
                            else if (ServerUtilities.msgIdToUserID[result.Msg_id] is NewOpenQuestion)
                            {
                                if (result.Match)
                                {
                                    ChatbotWebSocketController.SendAnswerToQuestion(new ChatbotNewAnswerModel(result));
                                }
                                else
                                {
                                    int    question_id = ProcessChatbotLogic.SaveQuestionToDatabase((NewOpenQuestion)ServerUtilities.msgIdToUserID[result.Msg_id]);
                                    String user_id     = ((NewOpenQuestion)ServerUtilities.msgIdToUserID[((MatchQuestionModelResponse)model.Value.First()).msg_id]).user_id;
                                    ChatbotWebSocketController.SendAnswerToQuestion(new ServerResponseNoAnswerToQuestion(result, (MatchQuestionModelResponse)model.Value.First(), question_id));
                                    ProcessChatbotLogic.AddNewOpenAnswer(user_id, question_id);
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.Write(e.Message);
                }
                break;

            case WEBSOCKET_RESPONSE_TYPE.OFFENSIVENESS:
            {
                try
                {
                    var result = ProcessNLPResponse.ProcessNLPOffensivenessResponse(model.Value.Cast <OffensivenessModelResponse>().ToList().First());
                    if (result is OffensivenessLogicResponse)
                    {
                        ProcessOffenseResult(result);
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.Write(e.Message);
                }
            }
            break;

            case WEBSOCKET_RESPONSE_TYPE.NONSENSE:
            {
                try
                {
                    NonsenseLogicResponse result = ProcessNLPResponse.ProcessNLPNonsenseResponse(model.Value.Cast <NonsenseModelResponse>().ToList().First());
                    if (result is NonsenseLogicResponse)
                    {
                        ProcessNonsenseResult(result);
                        //SendQuestionOffenseRequest((NonsenseLogicResponse)result);
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.Write(e.Message);
                }
            }
            break;
            }
        }
        /// <summary>
        /// Recursive function that looks for the best possible match that also has an aswer in the database
        /// </summary>
        /// <param name="match_candidates"></param>
        /// <param name="matchQuestionModel"></param>
        /// <returns></returns>
        private static MatchQuestionLogicResponse QueryHelperRecursive(List <MatchQuestionModelInfo> match_candidates, MatchQuestionModelResponse matchQuestionModel)
        {
            MatchQuestionModelInfo bestInfo = null;

            if (match_candidates.Count == 0)
            {
                return(new MatchQuestionLogicResponse()
                {
                    Msg_id = matchQuestionModel.msg_id
                });
            }
            else
            {
                // get best match of all candidates
                double bestMatch = 0.0;
                foreach (MatchQuestionModelInfo info in match_candidates)
                {
                    if (info.prob > bestMatch)
                    {
                        bestInfo  = info;
                        bestMatch = info.prob;
                    }
                }
            }

            if (bestInfo == null)
            {
                return(new MatchQuestionLogicResponse()
                {
                    Msg_id = matchQuestionModel.msg_id
                });
            }

            // Perform query to get the answer to the best match
            DBManager manager = new DBManager(true);

            MatchQuestionLogicResponse result = null;

            StringBuilder sb = new StringBuilder();

            sb.Append("SELECT answer ");
            sb.Append("FROM Answers a, Questions q ");
            sb.Append($"WHERE q.question_id = {bestInfo.question_id} AND q.answer_id = a.answer_id; ");
            String sql = sb.ToString();

            using (SqlDataReader reader = manager.Read(sql))
            {
                // This query should only return 0 or 1 result
                if (reader.Read())
                {
                    /****/
                    String sqlSafeAnswer = ServerUtilities.SQLSafeToUserInput(reader.GetString(0));
                    /****/
                    result = new MatchQuestionLogicResponse(matchQuestionModel.question_id, matchQuestionModel.msg_id, bestInfo.question_id, true, sqlSafeAnswer);
                }
            }

            manager.Close();

            // If a result is found, return the result, else look for another option that does have an answer
            if (result != null)
            {
                return(result);
            }
            else
            {
                match_candidates.Remove(bestInfo);
                return(QueryHelperRecursive(match_candidates, matchQuestionModel));
            }
        }