import random
from collections import deque
from maze import Maze, Cell

def empty_maze(width, height):
    maze = Maze(width, height)
    for x in range(width):
        for y in range(height):
            maze.grid[x][y].isWall = False
    maze.start = maze.getCell(0, 0)
    maze.start.isStart = True
    maze.exit = maze.getCell(width-1, height-1)
    maze.exit.isExit = True
    return maze

def random_maze(width, height, wall_prob=0.3, ensure_path=True):
    while True:
        maze = Maze(width, height)
        for x in range(width):
            for y in range(height):
                if random.random() < wall_prob:
                    maze.grid[x][y].isWall = True
                else:
                    maze.grid[x][y].isWall = False
        start_cell = maze.getCell(0, 0)
        exit_cell = maze.getCell(width-1, height-1)
        start_cell.isWall = False
        start_cell.isStart = True
        exit_cell.isWall = False
        exit_cell.isExit = True
        maze.start = start_cell
        maze.exit = exit_cell

        if not ensure_path:
            return maze

        if _path_exists(maze, start_cell, exit_cell):
            return maze

def no_path_maze(width, height):
    maze = empty_maze(width, height)
    exit_cell = maze.exit
    for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
        nx, ny = exit_cell.x + dx, exit_cell.y + dy
        neighbor = maze.getCell(nx, ny)
        if neighbor:
            neighbor.isWall = True
    return maze

def recursive_division_maze(width, height):
    if width % 2 == 0:
        width += 1
    if height % 2 == 0:
        height += 1

    maze = Maze(width, height)
    for x in range(width):
        for y in range(height):
            maze.grid[x][y].isWall = False

    maze.start = maze.getCell(0, 0)
    maze.start.isStart = True
    maze.exit = maze.getCell(width-1, height-1)
    maze.exit.isExit = True

    for x in range(width):
        maze.getCell(x, 0).isWall = True
        maze.getCell(x, height-1).isWall = True
    for y in range(height):
        maze.getCell(0, y).isWall = True
        maze.getCell(width-1, y).isWall = True

    maze.start.isWall = False
    maze.exit.isWall = False

    def divide(x1, y1, x2, y2):
        if x2 - x1 < 2 or y2 - y1 < 2:
            return

        vertical = (x2 - x1) > (y2 - y1) and (x2 - x1) >= 2
        if vertical:
            wall_x = random.randrange(x1 + 1, x2, 2)
            hole_y = random.randrange(y1, y2 + 1, 2) if (y2 - y1) > 0 else y1
            for y in range(y1, y2 + 1):
                if y != hole_y:
                    maze.getCell(wall_x, y).isWall = True

            divide(x1, y1, wall_x - 1, y2)
            divide(wall_x + 1, y1, x2, y2)
        else:
            wall_y = random.randrange(y1 + 1, y2, 2)
            hole_x = random.randrange(x1, x2 + 1, 2) if (x2 - x1) > 0 else x1
            for x in range(x1, x2 + 1):
                if x != hole_x:
                    maze.getCell(x, wall_y).isWall = True
            divide(x1, y1, x2, wall_y - 1)
            divide(x1, wall_y + 1, x2, y2)

    divide(0, 0, width-1, height-1)
    return maze

def _path_exists(maze, start, exit_cell):
    visited = set()
    queue = deque([start])
    visited.add(start)
    while queue:
        cur = queue.popleft()
        if cur == exit_cell:
            return True
        for n in maze.getNeighbors(cur):
            if n not in visited:
                visited.add(n)
                queue.append(n)
    return False