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


criteria = lambda _cur, _neighbor: _neighbor - _cur <= 1


def build_graph(mx):
    """
    This is the meat of the solution,
    getting the valid neighbors - and then weighing those neighbors as pathable
    - loop through each row/col and then find who that cell can navigate to, ULDR
    - if it can navigate, add an edge
    """
    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


def part1(mx):
    start = matrix.find_in_matrix(mx, "S")
    end = matrix.find_in_matrix(mx, "E")
    # Now that we got the start/end, fix those to 'a' and 'z' like the instructions said
    mx[start[0]][start[1]] = "a"
    mx[end[0]][end[1]] = "z"
    # Turn the a-z into numbers 0-25
    matrix.apply_to_all(mx, lambda x: ord(x) - ord("a"))

    graph = build_graph(mx)
    path = find_path(graph, start, end)
    print(len(path.nodes) - 1)


def part2(mx):
    end = matrix.find_in_matrix(mx, "E")
    # don't NEED this, because we go from all 'a', but need to replace it still
    s = matrix.find_in_matrix(mx, "S")
    # Now that we got the start/end, fix those to 'a' and 'z' like the instructions said
    mx[s[0]][s[1]] = "a"
    mx[end[0]][end[1]] = "z"
    starts = matrix.find_in_matrix(mx, "a", one=False)

    # Turn the a-z into numbers 0-25
    matrix.apply_to_all(mx, lambda x: ord(x) - ord("a"))
    graph = build_graph(mx)

    paths = []
    # Find the yx of all 'a'
    for start in starts:
        try:
            path = find_path(graph, start, end)
            paths.append(path)
        except algorithm.NoPathError:
            # This 'a' is in a puddle of 'c's, so it's not pathable
            pass
    shortest = min(paths, key=lambda x: x.total_cost)
    # matrix.apply_to_all(mx, lambda x: chr(x+ord('a'))) # Back to letters, single char prettier
    # all_visited = []
    # for p in paths:
    #    all_visited.extend(p.nodes)
    # all_visited = list(set(all_visited))
    # matrix.highlight(mx, red=all_visited, blink_green=shortest.nodes)
    print(shortest.total_cost)


def main():
    mx = matrix.load_matrix_file(shared.get_fname(12), matrix.split_word_to_chr_list)
    with shared.elapsed_timer() as elapsed:
        part1(mx)
        print(elapsed())
    mx = matrix.load_matrix_file(shared.get_fname(12), matrix.split_word_to_chr_list)
    with shared.elapsed_timer() as elapsed:
        part2(mx)
        print(elapsed())


if __name__ == "__main__":
    main()
