#!/usr/bin/env python

from random import randint
from re import split

from typeclasses.objects import Object
from commands.misc import CmdSetCat, CmdSetOctopus
from utils.word_list import routput


class NPC(Object):
    """
    An NPC is an NPC because it can react to what it _hears_.
    To do this, implement 'at_heard_say', a function that
    returns a string for a response.
    """
    def at_heard_say(self, message, from_obj,
                     is_say=True, is_whisper=False):
        "Override to return a string in response to message."
        pass

    def msg(self, text=None, from_obj=None, **kwargs):
        "Custom msg() method reacting to say."
        # make sure to not repeat what we ourselves said or we'll
        # create a loop
        if from_obj != self:
            is_say = False
            is_whisper = False
            try:
                say_text, is_say = text[0], text[1]['type'] == 'say'
                is_whisper = text[1]['type'] == 'whisper'
            except Exception:
                pass

            try:
                # message will be on the form `<Person> says, "say_text"`
                # we want to get only say_text without the quotes and any spaces
                message = text.split('says, ')[1].strip(' "')
                shout, response = self.at_heard_say(message, from_obj,
                                                    is_say, is_whisper)
                if response != None:
                    self.at_say(response, just_owner=(shout == "shout"))
            except Exception:
                pass

        # 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)


class CarriableNPC(NPC):
    """
    A carriable NPC is like any other NPC, except that since it can
    be carried and isn't locked down, it can't hear conversation in a
    room.
    """
    def at_say(self, message, msg_self=None, msg_location=None,
               receivers=None, msg_receivers=None, just_owner=True,
               **kwargs):
        "Does the best it can to speak out loud."
        owner = self.location

        if self.location.is_typeclass("typeclasses.rooms.Room"):
            super().at_say(message, msg_self=msg_self,
                           msg_location=msg_location,
                           receivers=receivers,
                           msg_receivers=msg_receivers)
        elif just_owner:
            owner.msg(f"The {self.name} says, \"{message}\"")
        else:
            owner.msg(
                f"The {self.name}, you are carrying, says, \"{message}\"")
            owner.location.msg(
                f"The {self.name}, carried by {self.location.name}, says, \"{message}\"", exclude=owner)


class Familiar(NPC):
    """
    Parent class for all familiars.

    These are pets that can be controlled by their owner to do antics.
    """
    def at_do(self, owner, antics):
        """
        Issue a 'send_emote' into the room with a antic.
        """
        def new_name(name):
            """
            Take a long name, like: fat, black cat
            And return _part_ of the name, like:

              - fat, black cat
              - black cat
              - cat
            """

            parts = list(filter(lambda s: s != "", split(r"[, ]+", name)))

            num_adjs = len(parts)-1
            lst_adjs = parts[randint(0, num_adjs):num_adjs]

            noun = parts[-1]
            adjs = ', '.join(lst_adjs)
            return f"{adjs} {noun}" if len(adjs) > 0 else noun

        # The familiar's name:
        name = self.db._sdesc or self.name

        if antics.startswith("'"):
            owner.announce_action(f"$Your() {new_name(name)}{antics}")
        else:
            owner.announce_action(f"$Your() {new_name(name)} {antics}")


class Cat(Familiar):
    """
    A puppetable 'cat' that acts based on that 'command'.
    """
    def at_object_creation(self):
        "Called when a cat is first created."
        self.cmdset.add(CmdSetCat, persistent=True)


class Octopus(Familiar):
    """
    A puppetable 'octopus' that acts based on that 'command'.
    """
    def at_object_creation(self):
        "Called when a octopus is first created."
        self.cmdset.add(CmdSetOctopus, persistent=True)
