#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri May 17 05:59:08 2019

@author: kazzastic
"""
from mazelib import *
import numpy as np
import cv2
from random import randrange
from random import choice, shuffle
import cython
if not cython.compiled:
    from mazelib.solve.MazeSolveAlgo import MazeSolveAlgo

class Maze(object):
    def __init__(self):
        self.generator = None
        self.grid = arr
        self.start = None
        self.end = None
        self.solver = WallFollower()
        self.solutions = None
        
    def generate_entrances(self, start_outer=True, end_outer=True):
        """ Generate maze entrances.
            Entrances can be on the walls, or inside the maze.
        """
        if start_outer and end_outer:
            self._generate_outer_entrances()
        elif not start_outer and not end_outer:
            self._generate_inner_entrances()
        elif start_outer:
            self.start, self.end = self._generate_opposite_entrances()
        else:
            self.end, self.start = self._generate_opposite_entrances()

        # the start and end shouldn't be right next to each other
        if abs(self.start[0] - self.end[0]) + abs(self.start[1] - self.end[1]) < 2:
            self.generate_entrances(start_outer, end_outer)    
    
    def _generate_outer_entrances(self):
        """ Generate maze entrances, along the outer walls. """
        H = self.grid.shape[0]
        W = self.grid.shape[1]

        start_side = randrange(4)

        # maze entrances will be on opposite sides of the maze.
        if start_side == 0:
            self.start = (0, randrange(1, W, 2))  # North
            self.end = (H - 1, randrange(1, W, 2))
        elif start_side == 1:
            self.start = (H - 1, randrange(1, W, 2))  # South
            self.end = (0, randrange(1, W, 2))
        elif start_side == 2:
            self.start = (randrange(1, H, 2), 0)  # West
            self.end = (randrange(1, H, 2), W - 1)
        else:
            self.start = (randrange(1, H, 2), W - 1)  # East
            self.end = (randrange(1, H, 2), 0)

    def _generate_inner_entrances(self):
        """ Generate maze entrances, randomly within the maze. """
        H, W = self.grid.shape

        self.start = (randrange(1, H, 2), randrange(1, W, 2))
        end = (randrange(1, H, 2), randrange(1, W, 2))

        # make certain the start and end points aren't the same
        while end == self.start:
            end = (randrange(1, H, 2), randrange(1, W, 2))

        self.end = end

    def solve(self):
        """ public method to solve a new maze, if possible """
        #if self.generator is None:
            #raise UnboundLocalError('No maze-solving algorithm has been set.')
        if self.start is None or self.end is None:
            raise UnboundLocalError('Start and end times must be set first.')
        else:
            self.solutions = self.solver.solve(self.grid, self.start, self.end)

    def tostring(self, entrances=False, solutions=False):
        """ Return a string representation of the maze. """
        if self.grid is None:
            return ''

        # build the walls of the grid
        txt = []
        for row in self.grid:
            txt.append(''.join(['#' if cell else ' ' for cell in row]))

        # insert the start and end points
        if entrances and self.start and self.end:
            r, c = self.start
            txt[r] = txt[r][:c] + 'S' + txt[r][c+1:]
            r, c = self.end
            txt[r] = txt[r][:c] + 'E' + txt[r][c+1:]

        # if extant, insert the solution path
        if solutions and self.solutions:
            for r, c in self.solutions[0]:
                txt[r] = txt[r][:c] + '+' + txt[r][c+1:]

        return '\n'.join(txt)

    def __str__(self):
        """ display maze walls, entrances, and solutions, if available """
        return self.tostring(True, True)

    def __repr__(self):
        return self.__str__()


class WallFollower(MazeSolveAlgo):
    """
    The Algorithm

    Follow the right wall and you will eventually end up at the end.

    details:

    1. Choose a random starting direction.
    2. At each intersection, take the rightmost turn. At dead-ends, turn around.
    3. If you have gone more than (H * W) + 2 cells, stop; the maze will not be solved.
    4. Terminate when you reach the end cell.
    5. Prune the extraneous branches from the solution before returning it.

    Optional Parameters

    Turn: String ['left', 'right']
        Do you want to follow the right wall or the left wall? (default 'right')
    """

    def __init__(self, turn='right', prune=True):
        # turn can take on values 'left' or 'right'
        if turn == 'left':
            self.directions = [(-2, 0), (0, -2), (2, 0), (0, 2)]
        else:  # default to right turns
            self.directions = [(-2, 0), (0, 2), (2, 0), (0, -2)]

        self.prune = prune

    def _solve(self):
        solution = []
        current = self.start

        # a first move has to be made
        if self._on_edge(self.start):
            current = self._push_edge(self.start)
            solution.append(current)

        # pick a random direction and move
        last = current
        current = choice(self._find_neighbors(last, False))
        last_diff = (current[0] - last[0], current[1] - last[1])
        last_dir = self.directions.index(last_diff)

        # add first move to solution
        solution.append(self._midpoint(last, current))
        solution.append(current)

        # now that you have set up the situation, follow the walls
        solution = self._follow_walls(last_dir, current, solution)

        if self.prune:
            solution = self._prune_solution(solution)

        solution = self._fix_entrances(solution)

        return [solution]

    def _follow_walls(self, last_dir, current, solution):
        """Perform the wall following logic.
        Loop until you have found the end,
        or prove you won't solve the maze.
        """
        limit = self.grid.shape[0] * self.grid.shape[1] + 2

        while len(solution) < limit:
            last_dir, temp = self._follow_one_step(last_dir, current)
            # the solution should not include the end point
            if temp == self.end:
                midpoint = self._midpoint(temp, current)
                if midpoint != self.end:
                    solution.append(midpoint)
                break
            solution.append(self._midpoint(temp, current))
            solution.append(temp)
            current = temp

        if len(solution) >= limit:
            raise RuntimeError('This algorithm was not able to converge on a solution.')

        return solution

    def _follow_one_step(self, last_dir, current):
        """At each new cell you reach, take the rightmost turn.
        Turn around if you reach a dead end.
        if right is not available, then straight, if not straight, left, etc...
        """
        for d in range(4):
            next_dir = (last_dir - 1 + d) % 4
            next_cell = self._move(current, self.directions[next_dir])
            r, c= self._midpoint(next_cell, current)

            if self.grid[r, c] == 0 and (r, c) != self.start:
                return (next_dir, next_cell)
            elif (r, c) == self.end:
                return (next_dir, self.end)

        return (last_dir, current)

    def _fix_entrances(self, solution):
        """Ensure the start and end are appropriately placed in the solution."""
        # prune if start is found in solution
        if self.start in solution:
            i = solution.index(self.start)
            solution = solution[i+1:]

        # prune if first position is repeated
        if solution[0] in solution[1:]:
            i = solution[1:].index(solution[0])
            solution = solution[i+1:]

        # prune duplicate end points
        if len(solution) > 1 and solution[-2] == solution[-1]:
            solution = solution[:-1]

        return solution


file = 'mazegreen.jpg'
img = cv2.imread(file)
cv2.imshow('The Initial', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
    
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray', gray_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
    
blurred = cv2.GaussianBlur(gray_img, (5,5), 0)
cv2.imshow('Blur', blurred)
cv2.waitKey(0)
cv2.destroyAllWindows()
    
(t, thresh) = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY)
cv2.imshow('Threshold Binary', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

arr = np.array(thresh)

for i in range(0, 293):
    for j in range(0, 257):    
        if(arr[i][j] == 255):        
            arr[i][j] = 1

canny = cv2.Canny(blurred, 30, 150)
cv2.imshow('Canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
cnts = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
new = img.copy()
cv2.drawContours(new, cnts, -1, (0,255,0),2)
cv2.imshow('Cont', new)
cv2.waitKey(0)
cv2.destroyAllWindows()



for i in range(0, 293):
    for j in range(0, 257):    
        if(arr[i][j] == 1):        
            arr[i][j] = 255

cv2.imshow('Result', arr)
cv2.waitKey(0)
cv2.destroyAllWindows()
            
            
'''          
my_maze = Maze()
#my_maze.solver = WallFollower()
my_maze.generate_entrances(start_outer=True, end_outer=True)
my_maze.solve()
'''