from ..interfaces import FriendlyEntity, InventoryHolder, FightingEntity, Map
from ..translations import gettext as _
from .player import Player
from .monsters import Monster
from .items import Item
from random import choice, shuffle


class Merchant(InventoryHolder, FriendlyEntity):
    """
    The class of merchants in the dungeon.
    """
    def keys(self) -> list:
        """
        Returns a friendly entitie's specific attributes.
        """
        return super().keys() + ["inventory", "hazel"]

    def __init__(self, name: str = "merchant", inventory: list = None,
                 hazel: int = 75, *args, **kwargs):
        super().__init__(name=name, *args, **kwargs)
        self.inventory = self.translate_inventory(inventory or [])
        self.hazel = hazel
        if not self.inventory:
            for i in range(5):
                self.inventory.append(choice(Item.get_all_items())())

    def talk_to(self, player: Player) -> str:
        """
        This function is used to open the merchant's inventory in a menu,
        and allows the player to buy/sell objects.
        """
        return _("I don't sell any squirrel")

    def change_hazel_balance(self, hz: int) -> None:
        """
        Changes the number of hazel the merchant has by hz.
        """
        self.hazel += hz


class Sunflower(FriendlyEntity):
    """
    A friendly sunflower.
    """
    def __init__(self, maxhealth: int = 15,
                 *args, **kwargs) -> None:
        super().__init__(name="sunflower", maxhealth=maxhealth, *args, **kwargs)

    @property
    def dialogue_option(self) -> list:
        """
        Lists all that a sunflower can say to the player.
        """
        return [_("Flower power!!"), _("The sun is warm today")]


class Familiar(FightingEntity):
    """
    A friendly familiar that helps the player defeat monsters.
    """
    def __init__(self, maxhealth: int = 25,
                 *args, **kwargs) -> None:
        super().__init__(maxhealth=maxhealth, *args, **kwargs)
        self.target = None

    def act(self, p: Player, m: Map) -> None:
        """
        By default, the familiar tries to stay at distance at most 2 of the
        player and if a monster comes in range 3, it focuses on the monster
        and attacks it.
        """
        if self.target is None:
            self.target = p
        if self.target == p:
            for entity in m.entities:
                if (self.y - entity.y) ** 2 + (self.x - entity.x) ** 2 <= 9 and\
                        isinstance(entity, Monster):
                    self.target = entity
                    entity.paths = dict()  # Allows the paths to be calculated.
                    break

        # Familiars move according to a Dijkstra algorithm
        # that targets their target.
        # If they can not move and are already close to their target,
        # they hit, except if their target is the player.
        if self.target and (self.y, self.x) in self.target.paths:
            # Moves to target player by choosing the best available path
            for next_y, next_x in self.target.paths[(self.y, self.x)]:
                moved = self.check_move(next_y, next_x, True)
                if moved:
                    break
                if self.distance_squared(self.target) <= 1 and \
                        not isinstance(self.target, Player):
                    self.map.logs.add_message(self.hit(self.target))
                    if self.target.dead:
                        self.target.paths = None
                        self.target = None
                    break
        else:
            # Moves in a random direction
            # If the direction is not available, tries another one
            moves = [self.move_up, self.move_down,
                     self.move_left, self.move_right]
            shuffle(moves)
            for move in moves:
                if move():
                    break


class Trumpet(Familiar):
    """
    A class of familiars.
    """
    def __init__(self, name: str = "trumpet", strength: int = 3,
                 maxhealth: int = 20, *args, **kwargs) -> None:
        super().__init__(name=name, strength=strength,
                         maxhealth=maxhealth, *args, **kwargs)