class DialogManager:
    def __init__(self, complete_db_overhaul=False):
        self.dialog_tracer_obj = DialogTracer(True)
        self.task_manager_obj = TaskManager(complete_db_overhaul)
        self.dialog_tracer_obj.sys_msg("Dialog Manager setup complete..")

    # Based on Food Tokens narrow the Food item and return the List
    def get_all_usda_food_items_from_user(self, food_items_tokens):
        self.task_manager_obj.text_to_audio(
            'Let me get more information from you about these items to figure out '
            'your exact intake')
        result_list = []
        experience = 1
        counter = 1
        counter_dict = {
            1: 'FIRST',
            2: 'SECOND',
            3: 'THIRD',
            4: 'FOURTH',
            5: 'FIFTH'
        }
        while len(food_items_tokens) != 0:
            # S = "======================== SELECTION OF ",counter_dict[counter]," FOOD ITEM: BEGIN ======================"
            self.dialog_tracer_obj.sys_msg(
                "======================== SELECTION OF " +
                counter_dict[counter] +
                " FOOD ITEM: BEGIN ======================")
            count = self._get_current_food_item_from_user(
                food_items_tokens, counter)
            counter += 1
            result = self.get_standard_item(food_items_tokens[:count],
                                            experience)
            experience += 1
            del food_items_tokens[:count]
            if result != "null":
                result_list.append(result)
                self.dialog_tracer_obj.sys_msg(
                    "======================== SELECTION OF " +
                    counter_dict[counter - 1] +
                    " FOOD ITEM: END ======================")
        return result_list

    # Finalizing the Food item if narrowed to less than equal to three item
    def _get_current_food_item_from_user(self, food_items_tokens, counter):
        counter_dict = {
            1: 'first',
            2: 'second',
            3: 'third',
            4: 'fourth',
            5: 'fifth'
        }
        if len(food_items_tokens) == 1:
            return 1
        if len(food_items_tokens) == 2:
            self.task_manager_obj.text_to_audio(
                'Please help me finalize your {0} food item. Say, \"I had the first item\" for {1}, or, '
                '\"I had the second item\" for {1} {2}'.format(
                    counter_dict[counter], food_items_tokens[0],
                    food_items_tokens[1]))
        else:
            self.task_manager_obj.text_to_audio(
                'Please help me finalize your {0} food item. Say, \"I had the first item\" for {1}, or, '
                '\"I had the second item\" for {1} {2}, or, \"I had the third item\" '
                'for {1} {2}  {3}'.format(counter_dict[counter],
                                          food_items_tokens[0],
                                          food_items_tokens[1],
                                          food_items_tokens[2]))
        result = self._get_current_food_combination_from_user()
        return result

    #
    def _get_current_food_combination_from_user(self):
        user_input = self.task_manager_obj.audio_to_text()
        user_input = user_input.lower().strip()
        if any(word in user_input for word in ['first', 'second', 'third']):
            self.task_manager_obj.text_to_audio('Thanks for your response')
        else:
            self.task_manager_obj.text_to_audio(
                'I could not understand, please provide your choice again.')
            user_input = self.task_manager_obj.audio_to_text()
            user_input = user_input.lower().strip()
            if any(word in user_input
                   for word in ['first', 'second', 'third']):
                self.task_manager_obj.text_to_audio('Thanks for your response')
        return 3 if 'third' in user_input else 2 if 'second' in user_input else 1

    # Get Name from User
    def get_user_name_from_user(self):
        self.dialog_tracer_obj.sys_msg(
            "======================== EXTRACT USER NAME: BEGIN ======================"
        )
        self.task_manager_obj.text_to_audio('Hello! Who am I speaking to?')
        user_input = self.task_manager_obj.audio_to_text()
        google_cloud_result = self.task_manager_obj.extract_user_name_from_text(
            user_input)
        if len(google_cloud_result) > 0:
            name = google_cloud_result[0].split()[0]
            self.task_manager_obj.text_to_audio(
                '{0}, Did I get your name right? '
                'Say yes to confirm, No to try again'.format(name))
            result = self.task_manager_obj.audio_to_text()
            result = result.lower().strip()
            if len(result) == 0:
                self.task_manager_obj.text_to_audio(
                    'I could not understand your response. '
                    'Say yes if I correctly identified your name. '
                    'Say No otherwise')
                result = self.task_manager_obj.audio_to_text()
                result = result.lower().strip()
            if any(word in result for word in [
                    'yes', 'yeah', 'yup', 'yep', 'yo', 'ok', 'okay', 'right',
                    'correct', 'sure'
            ]):
                self.task_manager_obj.text_to_audio(
                    'Great! Nice to meet you, {0}'.format(name))
                self.dialog_tracer_obj.sys_msg(
                    'Final user_name: {0}'.format(name))
                self.dialog_tracer_obj.sys_msg(
                    "======================== EXTRACT USER NAME: END ======================"
                )
                return name
        self.task_manager_obj.text_to_audio(
            'Sorry! I did not understand. Please tell me your name in a sentence.'
        )
        user_input = self.task_manager_obj.audio_to_text()
        google_cloud_result = self.task_manager_obj.extract_user_name_from_text(
            user_input)
        if len(google_cloud_result) > 0:
            name = google_cloud_result[0].split()[0]
            self.task_manager_obj.text_to_audio(
                '{0}, Did I get it right this time? Say yes to confirm.'.
                format(name))
            result = self.task_manager_obj.audio_to_text()
            result = result.lower().strip()
            if len(result) == 0:
                self.task_manager_obj.text_to_audio(
                    'I could not understand your response. '
                    'Say yes if I correctly identified your name.'
                    'Say No otherwise')
                result = self.task_manager_obj.audio_to_text()
                result = result.lower().strip()
            if any(word in result for word in [
                    'yes', 'yeah', 'yup', 'yep', 'yo', 'ok', 'okay', 'right',
                    'correct', 'sure'
            ]):
                self.task_manager_obj.text_to_audio(
                    'Great! Nice to meet you, {0}'.format(name))
                self.dialog_tracer_obj.sys_msg(
                    'Final user_name: {0}'.format(name))
                self.dialog_tracer_obj.sys_msg(
                    "======================== EXTRACT USER NAME: END ======================"
                )
                return name
        self.task_manager_obj.text_to_audio(
            'Sorry! I could not understand your name.'
            'For now, let me address you as, User')
        self.dialog_tracer_obj.sys_msg('Final user_name: {0}'.format("USER"))
        self.dialog_tracer_obj.sys_msg(
            "========================= EXTRACT USER NAME: END ======================"
        )
        return 'user'

    # Ask user for the food he had and return the Food Item tokens from it
    def get_food_items_tokens_from_user(self, user_name):
        self.dialog_tracer_obj.sys_msg(
            "========================= EXTRACT FOOD TOKENS : BEGIN ======================"
        )
        food_tokens = self._get_food_consumption_from_user(user_name, False)
        satisfied = self._confirm_food_items_from_user(user_name, food_tokens)

        if not satisfied:
            # food_tokens, food_token_text = self._get_food_consumption_from_user(user_name, True)
            food_tokens = self._get_food_consumption_from_user(user_name, True)
            satisfied = self._confirm_food_items_from_user(
                user_name, food_tokens)

        if not satisfied:
            self.task_manager_obj.text_to_audio(
                'I am sorry {0}, But the food items mentioned by you did not match '
                'my knowledge'.format(user_name))
            sys.exit()
            self.dialog_tracer_obj.sys_msg(
                "========================= EXTRACT FOOD TOKENS : END ======================"
            )
        return food_tokens

    #
    def _get_food_consumption_from_user(self, user_name, second):
        if second:
            self.task_manager_obj.text_to_audio(
                ' {0}, Mention the food items you had today with more detail'.
                format(user_name))
        else:
            self.task_manager_obj.text_to_audio(
                'Let us work on your caloric intake. What did you eat today, {0}?'
                .format(user_name))
        google_cloud_result = self.task_manager_obj.audio_to_text().lower()
        food_tokens = self.task_manager_obj.get_tokens_from_audio(
            google_cloud_result)

        if len(food_tokens) == 0:
            self.task_manager_obj.text_to_audio(
                'Sorry {0}, can you repeat the food item names again.'.format(
                    user_name))
            google_cloud_result = self.task_manager_obj.audio_to_text().lower()
            food_tokens = self.task_manager_obj.get_tokens_from_audio(
                google_cloud_result)

        if len(food_tokens) == 0:
            self.task_manager_obj.text_to_audio(
                'I am sorry {0}, But the food items mentioned by you did not match '
                'my knowledge'.format(user_name))
            sys.exit()
        return food_tokens

    # Confirm the tokens extracted are the food item he had
    def _confirm_food_items_from_user(self, user, food_tokens):
        output = self.task_manager_obj.refractor_tokens_to_spoken_string(
            food_tokens)
        self.task_manager_obj.text_to_audio(
            'Based on My Knowledge, these are the food items consumed '
            'by you: {0}'.format(output))
        self.dialog_tracer_obj.sys_msg(
            'FINAL FOOD ITEM TOKENS:::: [{0}]'.format(output))
        self.task_manager_obj.text_to_audio(
            '{0},Please say yes, if all the food items are covered.'.format(
                user))
        result = self.task_manager_obj.audio_to_text()
        result = result.lower().strip()
        if any(word in result for word in [
                'yes', 'yeah', 'yup', 'yep', 'yo', 'ok', 'okay', 'right',
                'correct', 'sure'
        ]):
            return True
        return False

    # Generate List of Contender for the Match and also get additional Token for
    # the item asking user for more information by giving Hints
    def get_usda_obj(self, food_item_token):
        token_set = set()
        for i in range(0, len(food_item_token)):
            token_set.add(food_item_token[i])
        s = ""
        for item in food_item_token:
            s = s + item
            s += " "
        result = self.task_manager_obj.get_fully_matched_food_results(
            token_set)
        if len(result) == 0:
            self.task_manager_obj.text_to_audio(
                "Sorry, I could not find any information for your selection, {0}. "
                "Let us move to the next item".format(s))
            return token_set, {}, result
        # print(result)
        dict_freq = self.task_manager_obj.freq_generator(result, token_set)
        sorted_x = sorted(dict_freq.items(), key=operator.itemgetter(1))
        high_freq_item = []
        for item in sorted_x:
            high_freq_item.append(item[0])
        # print (sorted_x)
        # print (high_freq_item)
        if len(high_freq_item) > 3:
            high_freq_item = high_freq_item[-3:]
        # print (high_freq_item)

        descriptors = ""
        for word in high_freq_item:
            descriptors = descriptors + word
            descriptors += "  ,  "
        self.dialog_tracer_obj.sys_msg(descriptors)
        if len(descriptors) == 0:
            return token_set
        self.task_manager_obj.text_to_audio(
            'For your selection {0}, would you like to provide some description? '
            'For instance, you could tell me which restaurant you ate it from, or '
            'provide preparation information like raw, or cooked, boiled, or fried,'
            'roasted or grilled etcetra. Few suggestions for your current '
            'selection are: '
            '{1}.. Say, \'No description\' if you want to skip'.format(
                s, descriptors))
        google_cloud_result = self.task_manager_obj.audio_to_text().lower()
        if any(word in google_cloud_result for word in
               ['no description', 'nope', 'no'
                'no, thanks', 'no, thank you']):
            self.task_manager_obj.text_to_audio(
                'Thanks. Let\'s proceed further.')
            return token_set, dict_freq, result
        token = self.task_manager_obj.get_tokens_from_audio(
            google_cloud_result)
        if len(google_cloud_result) == 0 or len(token) == 0:
            self.task_manager_obj.text_to_audio(
                'Sorry, I could not find matches for your description. '
                'Please try explaining in a sentence. For example, \'I '
                'ate my dish at Burger King\', or, \' My chicken was fried.\'.. Say, \''
                'No description\' if you want to skip')
            google_cloud_result = self.task_manager_obj.audio_to_text().lower()
        if any(word in google_cloud_result for word in
               ['no description', 'nope', 'no'
                'no, thanks', 'no, thank you']):
            self.task_manager_obj.text_to_audio(
                'Thanks. Let\'s proceed further.')
            return token_set, dict_freq, result
        self.task_manager_obj.text_to_audio('Thanks. Let\'s proceed further.')
        token_set = token_set.union(set(token))
        temp_result = self.task_manager_obj.get_fully_matched_food_results(
            token_set)
        if len(temp_result) == 0:
            token_set = token_set.difference(set(token))
            for i in range(0, len(token)):
                token_set.add(token[i])
                temp_result = self.task_manager_obj.get_fully_matched_food_results(
                    token_set)
                if len(temp_result) == 0:
                    token_set.remove(token[i])
                else:
                    for element in temp_result:
                        result.append(element)
        else:
            result = temp_result
        dict_freq = self.task_manager_obj.freq_generator(result, token_set)
        self.dialog_tracer_obj.sys_msg(token_set)
        return token_set, dict_freq, list(set(result))

    # Throw user set of 3 most probable desriptors , and get feedback
    def ask_user_choice_from_items(self, item_list, contender_list, dict_freq,
                                   experience):
        item = str(item_list[0]) + "\n" + str(item_list[1]) + "\n" + str(
            item_list[2])
        item_1 = item_list[0][0]
        item_2 = item_list[1][0]
        item_3 = item_list[2][0]
        self.dialog_tracer_obj.sys_msg(item)
        #self.dialog_tracer_obj.sys_msg(item_list[0])
        #self.dialog_tracer_obj.sys_msg(item_list[1])
        #self.dialog_tracer_obj.sys_msg(item_list[2])
        contender_len = len(contender_list)
        display = "Contender List Strength : " + str(
            contender_len) + "\nSize of Dictionary : " + str(len(dict_freq))
        # self.dialog_tracer_obj.sys_msg("Contender List Strength : {0}".format(contender_len))
        # self.dialog_tracer_obj.sys_msg("Size of Dictionary : {0}".format(len(dict_freq)))
        self.dialog_tracer_obj.sys_msg(display)

        if (experience == 1):
            self.task_manager_obj.text_to_audio(
                'Currently we have {3} choices which match your food item description. '
                '. Say, \"My food item relates to first choice.\" for {0}.  Or, \"My food '
                'item relates to second choice.\" for {1}.  Or,  \"My food item relates '
                'to third choice.\" for {2} .  If you have no descriptor matches please '
                'Say, \" None of the these matches\" . If Not sure please Say \" I am not '
                'sure \"'.format(item_1, item_2, item_3, contender_len))
        else:
            self.task_manager_obj.text_to_audio('{3} Contender remain.'
                                                '. Say, first, for {0}.  Or,'
                                                'Second for {1}.  Or,  '
                                                'third for {2} '.format(
                                                    item_1, item_2, item_3,
                                                    contender_len))

        audio_response = self.task_manager_obj.audio_to_text()
        audio_response = audio_response.lower().strip()
        self.task_manager_obj.text_to_audio('Thanks for your response')
        if not ('first' in audio_response or 'second' in audio_response
                or 'third' in audio_response or 'none' in audio_response or
                ('sure' in audio_response and 'not' in audio_response)):
            self.task_manager_obj.text_to_audio(
                ' I could not understand your response, kindly speak again')
            audio_response = self.task_manager_obj.audio_to_text()
            audio_response = audio_response.lower().strip()
            self.task_manager_obj.text_to_audio('Thanks for your response')
        if 'first' in audio_response:
            return item_1
        if 'second' in audio_response:
            return item_2
        if 'third' in audio_response:
            return item_3
        if 'none' in audio_response:
            return 'none'
        if 'sure' in audio_response and 'not' in audio_response:
            return 'not sure'
        return 'not sure'

    # If the contender list is reduced to less than equal to three, throw three contender item to user
    # and ask to pick one
    def get_final_item(self, contender_list):
        if len(contender_list) == 2:
            self.task_manager_obj.text_to_audio(
                'Based on your input, two items best match your food item Say, \"My food item relates to '
                'first item.\" for {0}. Or Say, \"My food item relates to second item.\" for '
                '{1}'.format(contender_list[0], contender_list[1]))
            audio_response = self.task_manager_obj.audio_to_text()
            audio_response = audio_response.lower().strip()
            self.task_manager_obj.text_to_audio('Thanks for your response')
            if 'first' in audio_response:
                return contender_list[0]
            if 'second' in audio_response:
                return contender_list[1]
            return contender_list[0]
        else:
            self.task_manager_obj.text_to_audio(
                'Based on your input, three items best match your food item Say, \"My food item relates to '
                'first item.\" for {0}. Or Say, \"My food item relates to second item.\" for {1}.Or Say, '
                '\"My food item relates to third item.\" for '
                '{2}'.format(contender_list[0], contender_list[1],
                             contender_list[2]))
            audio_response = self.task_manager_obj.audio_to_text()
            audio_response = audio_response.lower().strip()
            self.task_manager_obj.text_to_audio('Thanks for your response')
            if 'first' in audio_response:
                return contender_list[0]
            if 'second' in audio_response:
                return contender_list[1]
            if 'third' in audio_response:
                return contender_list[2]
            return contender_list[0]

    # Get reponse from the user whether he selected a descriptor or not sure or none of these and
    # accordingly manage the freq dictionary And Contender List
    def get_response(self, sorted_list_freq, contender_list, dict_freq,
                     experience):
        if len(sorted_list_freq) < 3 or len(dict_freq) < 3 or len(
                contender_list) <= 3:
            if len(dict_freq) < 3 < len(contender_list):
                contender_list = contender_list[:3]
            return "-----", contender_list, dict_freq
        response = self.ask_user_choice_from_items(
            list(reversed(sorted_list_freq[-3:])), contender_list, dict_freq,
            experience)
        while response == 'none' or response == 'not sure':
            if response == 'none':
                contender_list, dict_freq = self.task_manager_obj.dict_contender_updator_for_none(
                    dict_freq, sorted_list_freq[-3:], contender_list)
                sorted_list_freq = sorted(dict_freq.items(),
                                          key=operator.itemgetter(1))
            else:
                if len(sorted_list_freq) >= 3:
                    for i in range(-3, 0):
                        del dict_freq[sorted_list_freq[i][0]]
                    sorted_list_freq = sorted_list_freq[:-3]
            if len(sorted_list_freq) < 3 or len(dict_freq) < 3 or len(
                    contender_list) <= 3:
                if len(dict_freq) < 3 < len(contender_list):
                    contender_list = contender_list[:3]
                return "-----", contender_list, dict_freq
            experience = experience + 1
            response = self.ask_user_choice_from_items(
                list(reversed(sorted_list_freq[-3:])), contender_list,
                dict_freq, experience)
        return response, contender_list, dict_freq

    # Iteratively tries to reduce the list of items unless the list is less than equal to 3
    def narrow_list(self, contender_list, dict_freq, experience):
        while len(contender_list) >= 3:
            sorted_list_freq = sorted(dict_freq.items(),
                                      key=operator.itemgetter(1))
            response, contender_list, dict_freq = self.get_response(
                sorted_list_freq, contender_list, dict_freq, experience)
            experience += 1
            if response == "-----":
                break
            contender_list, dict_freq = self.task_manager_obj.reduce_list(
                contender_list, response, dict_freq)
        if len(contender_list) == 1:
            return contender_list[0]
        result = self.get_final_item(contender_list)
        return result

    # Returning the Food item based on the Food Tokens
    def get_standard_item(self, food_items, experience):
        token_set, dict_freq, contender_list = self.get_usda_obj(food_items)
        if len(contender_list) == 0:
            return "null"
        result = self.narrow_list(contender_list, dict_freq, experience)
        self.dialog_tracer_obj.sys_msg(result)
        return result

    # Displaying the Calorific Intake for Each Item
    def show_food_items_with_calories(self, final_usda_food_items):
        self.dialog_tracer_obj.sys_msg(
            "======================== CALORIFIC INFORMATION : BEGIN ======================"
        )
        if len(final_usda_food_items) == 0:
            self.dialog_tracer_obj.sys_msg("No Item to Display")
        for food_item in final_usda_food_items:
            self.task_manager_obj.display_item(food_item)
        self.dialog_tracer_obj.sys_msg(
            "======================== CALORIFIC INFORMATION : END ======================"
        )