import shared
import matrix
from dijkstar import Graph, find_path, algorithm


#def dj_walk(mx, start, end):
#    def next_cost(c, val):
#        if val > c+1:
#            return 9999
#        else:
#            return val
#
#    start = start[1], start[0]
#    end = end[1], end[0]
#
#    # Dijkstra from RedBlobGames
#    frontier = PriorityQueue()
#    frontier.put(start, 0)
#    came_from = dict()
#    cost_so_far = dict()
#    came_from[start] = None
#    cost_so_far[start] = 0
#    last = None
#    while not frontier.empty():
#        x, y = frontier.get()
#        cur = (x, y)
#        if cur == end:
#            break
#        for _n in matrix.get_neighbors(mx, x=x, y=y, _dict=True):
#            nxt = (_n["x"], _n["y"])
#            v = _n["value"]
#            
#            new_cost = next_cost(cost_so_far[cur], v)
#            if nxt not in cost_so_far or new_cost < cost_so_far[nxt]:
#                cost_so_far[nxt] = new_cost
#                priority = new_cost
#                frontier.put(nxt, priority)
#                came_from[nxt] = cur
#                last = cur
#    print(len(cost_so_far))

criteria = lambda _cur, _neighbor: _neighbor - _cur <= 1
def build_graph(mx):
    graph = Graph()
    for y, row in enumerate(mx):
        for x, _ in enumerate(row):
            neighbors = matrix.valid_neighbors(mx, x=x, y=y, criteria=criteria)
            for neighbor in neighbors:
                graph.add_edge((y, x), (neighbor['y'],neighbor['x']), 1)
    return graph



#SEEN = []
#def walk(mx, y, x, seen=[]):
#    print(len(seen))
#    valid_next = matrix.valid_neighbors(mx, y, x, criteria=criteria)
#    print(valid_next)
#    matrix.highlight( mx, red=seen, green=[ *valid_next, ], blue=[ END, ],)
#    for next_yx in valid_next:
#        if next_yx not in seen:
#            seen.append(next_yx)
#            _y, _x = next_yx
#            walk(mx, _y, _x, seen)
#        else:
#            seen.append(next_yx)

def elev(yx):
    y,x = yx
    return mx[y][x]

def part1(mx):
    start = matrix.find_in_matrix(mx, "S")
    end = matrix.find_in_matrix(mx, "E")
    mx[start[0]][start[1]] = "a"
    mx[end[0]][end[1]] = "z"
    matrix.apply_to_all(mx, lambda x: ord(x) - ord('a'))

    #walk(mx, cur[0],cur[0], seen=[(cur[0],cur[1])])
    #dj_walk(mx, cur, END)
    graph = build_graph(mx)
    path = find_path(graph, start, end)
    print(len(path.nodes))


def part2(mx):
    end = matrix.find_in_matrix(mx, "E")
    s = matrix.find_in_matrix(mx, "S")
    mx[s[0]][s[1]] = "a"
    mx[end[0]][end[1]] = "z"

    starts = matrix.find_in_matrix(mx, 'a', one=False)
    matrix.apply_to_all(mx, lambda x: ord(x) - ord('a'))
    graph = build_graph(mx)

    n_counts = []
    for start in starts:
    #walk(mx, cur[0],cur[0], seen=[(cur[0],cur[1])])
    #dj_walk(mx, cur, END)
        try:
            path = find_path(graph, start, end)
            n_counts.append(len(path.nodes)-1)
        except algorithm.NoPathError:
            pass
    print(n_counts)
    print(min(n_counts))


def main():
    mx = matrix.load_matrix_file(shared.get_fname(12), matrix.split_word_to_chr_list)
    #part1(mx)
    part2(mx)


if __name__ == "__main__":
    main()
