#!/usr/bin/env python

"""
Knocker

The Object is the class for gatekeepers between rooms.

Use the ObjectParent class to implement common features for *all* entities
with a location in the game world (like Characters, Rooms, Exits).

"""

from evennia.utils import logger

from .objects import Object
from .characters import Character
from utils.word_list import routput

from random import choice, random
import re


class Unhider(Object):
    """
    This thing has the ability to "unhide" other things.
    The idea is .... ugh.
    @set thing/
    """
class Returnable(Object):
    """
    This object can't go far from one or two locations.

    """
    pass

class Knocker(Object):
    """
    Special object that listens to what is said in the room, and
    attempts to a _real_ NPC.

    """
    muffled_responses = [
        "Mmmuufffmm",
        "Mmmf?",
        "Mummffmmph",
        "Umfmuummmfff",
        "Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.",
    ]

    # Note that the responses are ordered from most specific to least.
    all_responses = [
        # The password must be the first entry here, as we "act" on it:
        ["whiske?y", [
            "Alright, I'll open the door for yah.",
            "I heard my favorite, let's get this door unlocked.",
        ]],
        [r"knocker|goblin", [
            "[Sorry.|What was that?|Did you say something?] I'm hard of hearing on account of the brass ears.",
            "Yes, I suppose I'm this amazing puzzle you get to in Chapter Three. Wait, does that mean I'm just an NPC?",
            "Who me? I thought you were talking to the goblin in the bushes.",
            "Why yes, I am hard of hearing.",
        ]],
        ["password", [
            "Of course this door is protected by a super complicated encrypted password.",
            "If I tell you, it wouldn't be a secret now.",
            "The password? You just have to guess.",
            "Well, I suppose I could give you a |bhint|n.",
        ]],
        ["hint", [
            "A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?"
        ]],
        ["riddle", [
            "Aged in barrels, smooth and neat, in a glass by the fire, I'm a treat.",
            "A scent of oak, a whiff of grain, a drop of expertise from the cask remains.",
            """
            In oaken halls, I sleep and bide,
            till I'm called to warm your insides...
            hrm, that's a little vague, but kinda nice.
            """,
            """
            Alright, alright, the riddle should be clever.
            It should refer to its golden hue, and I should make it obvious
            that it isn't gold, and adding a reference to quest of the Argonauts
            would be a complete red herring, and quite a mean thing to do,
            so I shouldn't add that,
            but what about barrels? Yeah, need to include barrels, but not the
            way Bilbo road barrels, for that was definitely intended as a misleading
            riddle for Smaug, but of course, a smart dragon would always figure such
            things out, wait, where was I?
            """
        ]],
        [r"\bhello|\bgreet|\bhey\b", [
            "How's it going?",
            "How's it?",
            "'Sup.",
            "How are you?",
        ]],
        [r"\bass\b", [
            "Why yes, I am made of brass."
        ]],
        [r"\block", [
            "Yes, I'm familiar with the door and the fact that it is locked.",
            "This locked door is to protect the theft of Dabbler's scones.",
        ]],
        [r"\bdoor\b", [
            "What door? I don't see a door. Ha!",
            "That's right, I am the clever puzzle hanging on a door.",
            "I'm hanging on a door? Really? Let's see, can I roll my eyes?",
            "Just to let you know, the door is locked.",
            """
            I shouldn['t|be quiet, and not] be telling you this,
            but I like the cut of your [suit|cloak|jib].
            So, you see, if you speak the |bpassword|n, wait, I've said too much.
            """
        ]],
        [r"\byes|yeah|yah\b", [
            "Really? You agree?",
            "Excellent",
        ]],
        [r"\bno|nope\b", [
            "Well, it's true. Just ask the [raven|trees|gnome].",
        ]],
        [r"\?$", [
            "What about [me|it|'im]?",
            "I dunno...",
        ]],
    ]

    after_unlocked_responses = [
        """
        We could [feign|pretend|play make-believe] and
        [carry on|continue|persist] this [conversation|charade],
        but we both know that |byou|n know the [password|secret|secret word|magic],
        and can go through the |bdoor|n now.
        """,
        """
        [Sure|Why not|Why yes], let's [feign|pretend|play make-believe that] you
        don't know the [password|secret|secret word|magic], and
        [carry on|continue|persist] this conversation.
        """,
    ]

    cant_hear_responses = [
        "[Sorry.|What was that?|Did you say something?] I'm hard of hearing on account of the brass ears.",
        "Brass ears. Yeah, not the best at hearing.",
        "Yeah, These brass ears don't hear much except for the |b[secret|magic|] password|n.",
    ]

    unknown_responses = [
        "Are you talking to me or the goblin [in the bushes|up the tree|behind the rock]?",
        "Knock, knock.",
        "What do you mean?",
        "Of course I like [hard candy|squirrels|apples]. Who [doesn't|wouldn't|do you know that doesn't]?",
        "No thank you, I can't eat [apples|kumquats|figs], what with the brass teeth and a lack of guts.",
        "Tea? While that would be [nice|sweet] of you, I can't really hold a cup.",
        "I'm fine, thanks. How are you?",
        "I'll say, [we have had|that is] a spell of weather.",
    ]

    def at_heard_say(self, message, from_obj):
        """
        A simple listener and response. This makes it easy to change for
        subclasses of NPCs reacting differently to says.

        """
        # message will be on the form `<Person> says, "say_text"`
        # we want to get only say_text without the quotes and any spaces
        message = message.split('says, ')[1].strip(' "')

        # Let's see if a keyword gives a good response:
        for idx, [regex, responses] in enumerate(self.all_responses):
            full_regex = r".*" + regex + r".*"
            if re.match(full_regex, message):
                # The first match is the password, so we set a tag on
                # the character, so that they can go through the door:
                if idx == 0:
                    from_obj.tags.add("open_red_door", category="mp")

                # If we have the ring in our mouth, we are muffled:
                if self.has("brass ring"):
                    return choice(self.muffled_responses)
                else:
                    return routput(choice(responses))

        # We can not do a random response. Of course, we are
        # pretending we are hard of hearing, so we don't spam the room
        # _every_ time:
        if random() * 100 < 45:
            if self.has("brass ring"):
                return choice(self.muffled_responses)

            # If a keyword was not spoken, we want to emphasize that
            # we are hard of hearing, and mention that every other
            # time, so we store in the database a setting to keep
            # track and alternate the responses:
            elif self.db.hard:
                self.db.hard = False;
                if from_obj.tags.get(key="open_red_door", category="mp"):
                    return routput(choice(self.after_unlocked_responses))
                else:
                    return routput(choice(self.unknown_responses))
            else:
                self.db.hard = True;
                return routput(choice(self.cant_hear_responses))

    def get_display_desc(self, looker, **kwargs):
        # Use this for random information instead of self.db.desc
        response = "In a shape of a bald goblin, the brass door knocker in the center of the red door"
        if self.has("brass ring"):
          response += " holds a ring in its mouth. "
          response += "[You think it|You are sure it|You could've sworn it|It] [just|] winked at you."
        else:
          response += " [smiles|looks] at you expectantly. "

        return routput(response)

    def at_desc(self, looker):
        return "what" # self.get_display_desc(looker)

    def get_display_things(self, looker):
        return ""


    def msg(self, text=None, from_obj=None, **kwargs):
        "Custom msg() method reacting to say."
        if from_obj != self:
            # make sure to not repeat what we ourselves said or we'll create a loop
            is_say = False
            is_whisper = False

            try:
                # debug(f"text[0]: {text[0]}, text[1]: {text[1]}")
                # if text comes from a say, `text` is `('say_text', {'type': 'say'})`
                say_text, is_say = text[0], text[1]['type'] == 'say'
                is_whisper = text[1]['type'] == 'whisper'
            except Exception:
                pass

            if is_whisper:
                self.at_say("I'm a little hard of hearing, can you speak up?")
            elif is_say:
                # First get the response (if any)
                response = self.at_heard_say(say_text, from_obj)
                # If there is a response
                if response != None:
                    self.at_say(response)

        # this is needed if anyone ever puppets this NPC - without it you would never
        # get any feedback from the server (not even the results of look)
        super().msg(text=text, from_obj=from_obj, **kwargs)

    def at_object_leave(self, obj, target_location, **kwargs):
        if obj.key == "brass ring" and target_location != self:
            self.at_say("Oh my, that feels better.")
        else:
            self.at_say("Umph")
        return True

    def knocked_timed_out(self):
        if self.has("brass ring"):
            self.at_say(choice(self.muffled_responses))
        else:
            self.at_say("Doesn't appear that anyone is home.")
