def preference_extractor(self, user_input):
        """
        Parameters
        ----------
        user_input : str
            user utterance.

        Returns
        -------
        user_preferences : list
            list of user preferences extracted from utterance.
            p[0] for the area, p[1] for the price range and p[2] for the food type

        """

        user_preferences = [
            0 for i in range(3)
        ]  #the preferences are stored in a list of three elements, p[0] for the area, p[1] for the price range and p[2] for the food type
        s = self.stopwords_removal(user_input)

        user_preferences = self.no_preference(
            user_input,
            user_preferences)  #check if user indicated no preference

        #keyword matching for the area
        for i in s:
            for j in self.area:
                if i == j:
                    user_preferences[0] = j
        if (('north' and 'american' in s) and (s.count('north')) > 1):
            user_preferences[0] = 0
        #keyword matching for the price range
        for i in s:
            for j in self.price_range:
                if i == j:
                    user_preferences[1] = j

        #keyword matching for the food type
        for i in s:
            for j in self.food_types:
                if i == j:
                    user_preferences[2] = j
                elif ('asian' and 'oriental' in s):
                    user_preferences[2] = 'asian oriental'
                elif ('modern' and 'european' in s):
                    user_preferences[2] = 'modern european'
                elif ('north' and 'american' in s):
                    user_preferences[2] = 'north american'

        #In case the area has been mispelt
        if (user_preferences[0] == 0):
            d = {}
            l = []
            z = ['south', 'centre', 'west', 'east', 'north']
            for i in s:
                for j in z:
                    if (dt(i, j) <= 2) and i != 'want' and i != 'eat':
                        d[j] = dt(i, j)
            for i in d.values():
                l.append(i)
            if len(l) > 0:
                k = min(l)
                key_list = list(d.keys())
                val_list = list(d.values())
                if k <= 2:
                    user_preferences[0] = key_list[val_list.index(k)]

        #In case the price range has been mispelt
        if (user_preferences[1] == 0):
            d = {}
            l = []
            d = {}
            l = []
            for i in s:
                for j in list(set(self.price_range)):
                    if (dt(i, j) <= 3):
                        d[j] = dt(i, j)
            for i in d.values():
                l.append(i)
            if len(l) > 0:
                k = min(l)
                key_list = list(d.keys())
                val_list = list(d.values())
                if k <= 2:
                    user_preferences[1] = key_list[val_list.index(k)]

        #In case the  food type has been mispelt
        #thresolds for Levenshtein distances might need to be better tuned for each preference
        if (user_preferences[2] == 0):
            d = {}
            l = []
            for i in s:
                for j in list(set(self.food_types)):
                    if (dt(i, j) <= 2):
                        d[j] = dt(i, j)
                    elif (dt('asian', i) <= 2 or dt('oriental', i) <= 2 in s):
                        d['asian oriental'] = min(
                            [dt('asian', i), dt('oriental', i)])
                    elif (dt('modern', i) <= 2 or dt('european', i) <= 2 in s):
                        d['modern european'] = min(
                            [dt('modern', i),
                             dt('european', i)])
                    elif (dt('north', i) <= 2 or dt('american', i) <= 2 in s):
                        if ('north' and 'american' in s):
                            d['north american'] = min(
                                [dt('north', i),
                                 dt('american', i)])
            for i in d.values():
                l.append(i)
            if len(l) > 0:
                k = min(l)
                key_list = list(d.keys())
                val_list = list(d.values())
                if k <= 3:
                    user_preferences[2] = key_list[val_list.index(k)]
        return user_preferences
    def dialogue(self, user_input, state, user_preferences):
        """
        recursive state transition function.

        Parameters
        ----------
        user_input : str
            DESCRIPTION.
        state : str
            State of the system.
        user_preferences : list
            list of user preferences (area,price,foodtype).

        Returns
        -------
        None.

        """

        if user_input in [
                "configure formal", "configure delay", "configure informal",
                "configure no delay"
        ]:
            self.configure(user_input)
            user_input = ""
            self.dialogue(user_input, state, user_preferences)

        time.sleep(self.delay)
        self.statelog.append(
            [user_input, state]
        )  #tuple of user utterance and its associated state. We use this to keep track of state jumps.

        if state == "exit":
            print("Dialog Agent: " +
                  random.choice(self.responses.get("Goodbye")))
            return

        if state in ("init"):
            user_preferences = [0, 0, 0]
            user_input = input("Dialog Agent: " +
                               random.choice(self.responses.get("Welcome")) +
                               "User: "******"inform", "reqalts", 'hello'):
            extracted_preferences = self.preference_extractor(user_input)
            for i, d in enumerate(user_preferences):
                if d == 0:
                    user_preferences[i] = extracted_preferences[i]

            state = "fill_blanks"  #if more slots to be filled
            self.suggestions = self.lookup(user_preferences)

            if (len(self.suggestions) == 0) or (len(self.suggestions) == 1):

                state = "answer"  #if there is none or 1 restaurant to suggest
            self.dialogue(user_input, state, user_preferences)
            return

        if state == "fill_blanks":  #ask user for area/foodtype/pricerange
            grounding = self.grounding(user_preferences)
            if user_preferences[0] == 0:
                user_input = input("Dialog Agent: " + grounding +
                                   random.choice(self.responses.get("Area")) +
                                   "User: "******"area" not in user_input:
                    user_input += " area"
                if "dont care" in user_input:
                    user_input = 'any area'
            elif user_preferences[1] == 0:
                user_input = input("Dialog Agent: " + grounding +
                                   random.choice(self.responses.get("Price")) +
                                   "User: "******"price" not in user_input:
                    user_input += " price"
                if "dont care" in user_input:
                    user_input = 'any price'
            elif user_preferences[2] == 0:
                user_input = input("Dialog Agent: " + grounding +
                                   random.choice(self.responses.get("Food")) +
                                   "User: "******"food" not in user_input:
                    user_input += " food"
                if "dont care" in user_input:
                    user_input = 'any food'
            else:
                state = 'ask_extra_preferences'
            self.dialogue(user_input, state, user_preferences)
            return

        if state == 'ask_extra_preferences':
            state = self.ask_extra_preferences(user_preferences)
            self.dialogue(user_input, state, user_preferences)
            return

        if state == "confirmpreferences":
            user_input = input(
                "Dialog Agent: " +
                random.choice(self.responses.get("AffirmPreferences")).format(
                    user_preferences[0], user_preferences[1],
                    user_preferences[2]) + "User: "******"answer"
            elif accept is False:
                state = "inform"
                user_input = ""
                user_preferences = [0, 0, 0]
            elif accept == "reqalts":
                user_preferences = [0, 0, 0]
            else:
                state = "accept"
            self.dialogue(user_input, state, user_preferences)
            return

        if state == "answer":
            if self.suggestions:  #found at least 1 restaurant
                user_input = input("Dialog Agent: " +
                                   self.suggest_restaurant() + "User: "******"ack", "affirm"]:
                    state = "goodbye"
                elif state in ["reqalts", "reqmore", "deny", "negate"]:
                    state = "answer"
            else:  #no restaurants found. Search for alternatives
                alternatives = self.get_alternative_restaurants(
                    self.alternative_preferences(
                        user_preferences))  #offer alternatives
                if len(alternatives) == 1:  #found 1 alternative
                    print("Dialog Agent: " +
                          random.choice(self.responses.get("NoOptions")) +
                          "Let me look for an alternative for you...\n")
                    self.suggestions = alternatives
                    self.recommendation = self.suggestions[0]
                    user_input = input("Dialog Agent: " +
                                       self.suggest_restaurant() + "User: "******"goodbye"
                elif alternatives:  #found multiple alternatives
                    print("Dialog Agent: " +
                          random.choice(self.responses.get("NoOptions")) +
                          "Here is a list of alternatives:")
                    for a in alternatives:
                        print("Dialog Agent: " + self.get_restaurant_info(a))
                    user_input = input(
                        "Dialog Agent: " +
                        'Would you like to choose one (1) or change your preferences(2)?\n'
                        + "User: "******"1":
                        user_input = input(
                            "Dialog Agent: " +
                            "Which one would you like to choose?\n" + "User: "******"thankyou"
                    elif user_input == "2":
                        user_preferences = [0, 0, 0]
                        state = 'inform'
                    elif user_input == "exit":
                        state = 'exit'
                    else:
                        print("Dialog Agent: " +
                              "Please choose one of the two options")
                else:  #didnt find any alternative
                    print("Dialog Agent: " +
                          random.choice(self.responses.get("NoOptions")))
                    user_preferences = [0, 0, 0]
                    state = 'inform'
                    user_input = ""
            self.dialogue(user_input, state, user_preferences)
            return

        if state in ["reqalts", "thankyou", "goodbye", "reset"]:

            user_input = input(
                "Dialog Agent: " +
                self.get_restaurant_contacts(self.recommendation) +
                ". Would you like to finish here?\n" + "User: "******"ack", "affirm")):
                state = "exit"
            else:
                state = "init"
            self.dialogue(user_input, state, user_preferences)
            return

        if state == "repeat":
            try:
                user_input = self.statelog[len(self.statelog) - 3][0]
                state = self.statelog[len(self.statelog) - 3][1]
            except IndexError:
                print("Dialog Agent: " +
                      "Nowhere to go back, starting again\n")
                state = "init"
            self.dialogue(user_input, state, user_preferences)
            return

        else:
            print(
                "Dialog Agent: " +
                "I could not understand that, could you phrase it differently?"
            )  #statelog[len(statelog) + 1][0]
            state = self.statelog[len(self.statelog) - 2][1]
            self.dialogue(user_input, state, user_preferences)
            return