import arcade, arcade.gui, time, random, json

from plyer import notification

class Game(arcade.gui.UIView):
    def __init__(self, pypresence_client):
        super().__init__()
        
        self.pypresence_client = pypresence_client
        self.pypresence_client.update(state="Solving a maze inside notifications!")

        self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
        self.info_label = self.anchor.add(arcade.gui.UILabel("Use arrow keys or WASD to move.\nMaze is shown in notifications.", font_size=24, multiline=True))

        with open("settings.json", "r") as file:
            self.settings = json.load(file)

        self.running = True
        self.time_start = time.perf_counter()

        self.current_position = arcade.math.Vec2(1, 1)
        self.direction = arcade.math.Vec2()
        
    def on_show_view(self):
        self.maze, start_x, start_y = self.generate_maze(int(self.settings.get("notification_cols", 25) / 2), int(self.settings.get("notification_rows", 20) / 2))
        self.current_position = arcade.math.Vec2(start_x, start_y)
        self.update()

    def update(self):
        if not self.running:
            return

        next_pos = self.current_position + self.direction
        nx, ny = int(next_pos.x), int(next_pos.y)

        if 0 <= ny < len(self.maze) and 0 <= nx < len(self.maze[0]):
            if self.maze[ny][nx] != "#":
                self.current_position = next_pos
                if self.maze[ny][nx] == "E":
                    self.running = False
                    elapsed = int(time.perf_counter() - self.time_start)

                    self.info_label.text = f"Maze completed in {elapsed}s! Press r to restart."

                    notification.notify(
                        title="Maze Completed!",
                        message=f"You reached the end in {elapsed}s!")
                    return

        display_rows = []
        for y, row in enumerate(self.maze):
            line = ""
            for x, ch in enumerate(row):
                if (x, y) == (int(self.current_position.x), int(self.current_position.y)):
                    line += "P"
                else:
                    line += ch
            display_rows.append(line)

        self.direction = arcade.math.Vec2()
        notification.notify(
            title=f"Maze | {int(time.perf_counter() - self.time_start)}s",
            message='\n'.join(display_rows),
            timeout=15
        )

    def on_key_press(self, symbol, modifiers):
        if symbol in (arcade.key.UP, arcade.key.W):
            self.direction = arcade.math.Vec2(0, -1)
            self.update()
        elif symbol in (arcade.key.DOWN, arcade.key.S):
            self.direction = arcade.math.Vec2(0, 1)
            self.update()
        elif symbol in (arcade.key.LEFT, arcade.key.A):
            self.direction = arcade.math.Vec2(-1, 0)
            self.update()
        elif symbol in (arcade.key.RIGHT, arcade.key.D):
            self.direction = arcade.math.Vec2(1, 0)
            self.update()
        elif symbol == arcade.key.R and not self.running:
            self.running = True
            self.time_start = time.perf_counter()

            self.info_label.text = "Use arrow keys or WASD to move.\nMaze is shown in notifications."
            
            self.maze, start_x, start_y = self.generate_maze(int(self.settings.get("notification_cols", 25) / 3), int(self.settings.get("notification_rows", 20) / 3))
            self.current_position = arcade.math.Vec2(start_x, start_y)
            self.update()

        elif symbol == arcade.key.ESCAPE:
            from menus.main import Main
            self.window.show_view(Main(self.pypresence_client))

    def generate_maze(self, width, height):
        maze = [['#' for _ in range(width)] for _ in range(height)]

        def is_valid(x, y):
            return 1 <= x < width - 1 and 1 <= y < height - 1

        def get_neighbors(x, y):
            neighbors = []
            
            for dx, dy in [(0, 2), (2, 0), (0, -2), (-2, 0)]:
                nx, ny = x + dx, y + dy

                if is_valid(nx, ny) and maze[ny][nx] == '#':
                    neighbors.append((nx, ny))

            return neighbors

        def carve(x, y):
            maze[y][x] = '_'

            neighbors = get_neighbors(x, y)
            random.shuffle(neighbors)

            for nx, ny in neighbors:
                if maze[ny][nx] == '#':
                    maze[y + (ny - y) // 2][x + (nx - x) // 2] = '_'
                    carve(nx, ny)

        start_x = 1 if width % 2 == 1 else 2
        start_y = 1 if height % 2 == 1 else 2
        
        carve(start_x, start_y)

        maze[start_y][0] = '_'

        for y in range(height - 2, 0, -1):
            if maze[y][width - 2] == '_':
                maze[y][width - 1] = '_'
                break

        maze[height - 2][width - 1] = 'E'

        return maze, start_x, start_y