from panda3d.core import *

from global_stuff import base, colors


class Tile:
    def __init__(self, x: int, y: int, grid, color: str):
        assert color in colors.keys()
        self._wall = False
        self._door = False
        self._door_open = False
        self._color = color
        self.x = x
        self.y = y
        self.letter = chr(ord('A')+y)
        self.grid = grid

        self.model = NodePath('tile_model')
        self.model.reparent_to(base.render)
        self.model.set_pos(x, grid.height-y+1, 0)

        self.cube_np = base.loader.load_model('model/tile')
        self.cube_np.set_color_scale(colors[color])
        self.cube_np.set_pos(0, 0, -0.025)
        self.cube_np.set_scale(0.9, 0.9, 0.05)
        self.cube_np.reparent_to(self.model)

        self.entity = None

        self.wall_model = None
        self._model_conn = -1

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value: str):
        assert value in colors.keys()
        self._color = value
        self.cube_np.set_color_scale(colors[value])

    @property
    def door(self) -> bool:
        return self._door

    @door.setter
    def door(self, val: bool):
        if val == self._door:
            return
        if self.entity:
            return
        conn = self.wall_connectivity()
        if conn not in (5, 10):  # ensure we have a wall on either side
            return
        for tile in self.cross_neighbors:
            if tile.door and tile.wall_connectivity() != conn:  # avoid perpendicular doors
                return
        self._door = val
        self._wall = val
        self._model_conn = -1
        self.update_wall()

    def open_door(self, val=True):
        if not self.door:
            return
        self._door_open = val
        self._model_conn = -1
        self.update_wall(False)

    @property
    def wall(self) -> bool:
        return self._wall

    @wall.setter
    def wall(self, val: bool):
        if val == self._wall and not self._door:
            return
        if self.entity:
            return
        self._model_conn = -1
        self._door = False
        self._wall = val
        self.update_wall()

    @property
    def impassable(self) -> bool:
        return self._wall and (not self.door or not self._door_open)

    def update_wall(self, alert_neighbors=True):
        if not self._wall:
            if self.wall_model:
                self.wall_model.remove_node()
                self.wall_model = None
        else:
            conn = self.wall_connectivity()
            if self._model_conn != conn:
                if self.door and conn not in (5, 10):
                    self.door = False
                    return
                if self.wall_model:
                    self.wall_model.remove_node()
                if self.door:
                    s = 'open' if self._door_open else 'closed'
                    self.wall_model = base.loader.load_model('model/door/'+s+'_'+str(conn))
                else:
                    self.wall_model = base.loader.load_model('model/walls/'+str(conn))
                self.wall_model.reparent_to(self.model)
                self.wall_model.set_pos(0, 0, -0.05)
                self._model_conn = conn
        if alert_neighbors:
            for n in self.cross_neighbors:
                n.update_wall(False)
            self.grid.update_wall(self)

    def wall_connectivity(self):
        conn = 0
        if self.y == self.grid.height or self.grid[self.x][self.y+1].wall:
            conn += 4
        if self.x == self.grid.width or self.grid[self.x+1][self.y].wall:
            conn += 2
        if self.y == 1 or self.grid[self.x][self.y-1].wall:
            conn += 1
        if self.x == 1 or self.grid[self.x-1][self.y].wall:
            conn += 8
        return conn

    @property
    def neighbors(self):
        valid_x = [0]
        valid_y = [0]
        if self.x > 1:
            valid_x.append(-1)
        if self.x < self.grid.width:
            valid_x.append(1)
        if self.y > 1:
            valid_y.append(-1)
        if self.y < self.grid.height:
            valid_y.append(1)

        for x in valid_x:
            for y in valid_y:
                if x == 0 and y == 0:
                    continue
                yield self.grid[self.x + x][self.y + y]

    @property
    def cross_neighbors(self):
        for n in self.horizontal_neighbors:
            yield n
        for n in self.vertical_neighbors:
            yield n

    @property
    def horizontal_neighbors(self):
        if self.x > 1:
            yield self.grid[self.x - 1][self.y]
        if self.x < self.grid.width:
            yield self.grid[self.x + 1][self.y]

    @property
    def vertical_neighbors(self):
        if self.y > 1:
            yield self.grid[self.x][self.y - 1]
        if self.y < self.grid.height:
            yield self.grid[self.x][self.y + 1]
