import abc

from mfg_package.environment import constants as c
from mfg_package.environment.entity.object import EnvObject
from mfg_package.utils.render import RenderEntity


class Entity(EnvObject, abc.ABC):
    """Full Env Entity that lives on the environment Grid. Doors, Items, DirtPile etc..."""

    @property
    def has_position(self):
        return self.pos != c.VALUE_NO_POS

    @property
    def x(self):
        return self.pos[0]

    @property
    def y(self):
        return self.pos[1]

    @property
    def pos(self):
        return self._tile.pos

    @property
    def tile(self):
        return self._tile

    @property
    def last_tile(self):
        try:
            return self._last_tile
        except AttributeError:
            # noinspection PyAttributeOutsideInit
            self._last_tile = None
            return self._last_tile

    @property
    def last_pos(self):
        try:
            return self.last_tile.pos
        except AttributeError:
            return c.VALUE_NO_POS

    @property
    def direction_of_view(self):
        last_x, last_y = self.last_pos
        curr_x, curr_y = self.pos
        return last_x - curr_x, last_y - curr_y

    def move(self, next_tile):
        curr_tile = self.tile
        if not_same_tile := curr_tile != next_tile:
            if valid := next_tile.enter(self):
                curr_tile.leave(self)
                self._tile = next_tile
                self._last_tile = curr_tile
                for observer in self.observers:
                    observer.notify_change_pos(self)
            return valid
        return not_same_tile

    def __init__(self, tile, **kwargs):
        super().__init__(**kwargs)
        self._tile = tile
        tile.enter(self)

    def summarize_state(self) -> dict:
        return dict(name=str(self.name), x=int(self.x), y=int(self.y),
                    tile=str(self.tile.name), can_collide=bool(self.can_collide))

    @abc.abstractmethod
    def render(self):
        return RenderEntity(self.__class__.__name__.lower(), self.pos)

    def __repr__(self):
        return super(Entity, self).__repr__() + f'(@{self.pos})'
