

# '''
# Assumptions:

# maze returned will be a grid - array consisting of 1 and 0
#     0 - cell is empty
#     1 - cell is wall

#     EXAMPLE:
#    1 1 1 1 1 1 1
#    0 0 0 0 1 0 0
#    1 1 1 0 1 0 1
#    1 0 0 0 1 0 1
#    1 0 1 1 1 0 1
#    1 0 0 0 0 0 1
#    1 1 1 1 1 1 1


#        self.maze = [ 1,1,1,1,1,1,1,1,1,1,
#                      1,0,0,0,0,0,0,0,0,1,
#                      1,0,0,0,0,0,0,0,0,1,
#                      1,0,1,1,1,1,1,1,0,1,
#                      1,0,1,0,0,0,0,0,0,1,
#                      1,0,1,0,1,1,1,1,0,1,
#                      1,0,0,0,0,0,0,0,0,1,
#                      1,1,1,1,1,1,1,1,1,1,]

#     alternative: array containing specific codes indicating on which side, cell has a wall, one for each cell
#     0 is it has none
#     cells on the side dont inform about external wall of the maze


# '''

# import numpy as np
# import random
# width = 10
# height = 10
# # directions
# N, S, E, W = 1, 2, 3, 4
# DIRECTIONS = [N, S, E, W]
# DX = {N: 0, S: 0, E: 1, W: -1}
# DY = {N: 1, S: -1, E: 0, W: 0}

# OPOSSITE_DIRECTION = {E: W, W: E, N: S, S: N}


# def valid_indexes(x, y, grid):
#     x_max, y_max = grid.shape

#     return x >= 0 and x < x_max and y >= 0 and y < y_max


# def was_visited(x, y, grid, value=0):
#     return grid[x][y] != value


# def find_path(x, y, grid):
#     # possible directions for each cell
#     directions = random.sample(DIRECTIONS, len(DIRECTIONS))

#     for direction in directions:
#         print(direction)
#         new_x, new_y = x+DX[direction], y+DY[direction]

#         # check if is valid direction
#         if valid_indexes(new_x, new_y, grid) and not was_visited(new_x, new_y, grid):
#             grid[x][y] = direction
#             grid[new_x][new_y] = OPOSSITE_DIRECTION[direction]
#             find_path(new_x, new_y, grid)


# grid = np.zeros((width, height))
# find_path(0, 0, grid)
# print(grid)

import random
import numpy as np


class Maze:

    """ Class representing maze 
        Maze is padded with cells representing walls.
        There is no start and no finish point since it was developed for path finding with dynamic end points, but they can be set easily with changing desired wall to be 0
    
    
    Attributes:

        grid (np.ndarray) - represents maze, by convention 1 represents wall and 0 represents an empty cell
        width (int): number of desired empty cells on x axis   
        height (int) number of desired empty cells on y axis
        grid_width (int): width of created ndarray
        grid_height (int): height of created ndarray

    
    """
    def __init__(self, width, height):
        
        #  Parameters:

        #   width(int): number of desired empty cells on x axis
        #   height(int) number of desired empty cells on y axis
        #   NOTE: grid dimension will be 2*width+1 and 2*height+1
        #
        self.width = width
        self.height = height
        self.grid_width = 2*width +1
        self.grid_height = 2*height +1
        self.grid = np.ones((self.grid_height, self.grid_width))  # full walls
        self.directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        self.create(1, 1)

    def create(self, x, y):
        """Creates maze with backtracking algorithm
        
        Parameters: 
        x (int): starting position in x axis
        y (int): starting position in y axis
"""
        self.set_as_path(x, y)

        random_directions = random.sample(
            self.directions, len(self.directions))

        for direction in random_directions:
            new_x = x+direction[0]*2
            new_y = y+direction[1]*2

            if self.valid_direction(new_x, new_y):
                beetwen_x = x+direction[0]
                beetwen_y = y+direction[1]

                self.set_as_path(beetwen_x, beetwen_y)
                self.create(new_x, new_y)

    def valid_direction(self, x, y):
        """Checks if passed cordinates are within matrix
        
        Parameters:
        x (int): cordinate on x axis
        y (int): cordinate on y axis
        
        """
        if x > 0 and x < self.grid_width and y > 0 and y < self.grid_height:
            return self.grid[x][y]

        return False

    def set_as_path(self, x, y):
        """Sets cell on x,y cordinates as empty

        Parameters:
        x (int): cordinate on x axis
        y (int): cordinate on y axis
        
        """
        self.grid[x][y] = 0

    def __str__(self) -> str:
        """Represents Maze as string"""

        output_string = ""
        style = {0: '  ', 1: '██'}

        for x in range(self.grid_width):
            output_string+=f'{x} '
        
        output_string+='\n'
        
        for y in range(self.grid_height):
            for x in range(self.grid_width):
                output_string += style[self.grid[y][x]]
            output_string += f"{y} \n"

        return output_string
