# First - initializing the graph
graph = {}

# Put first element in graph
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

# Put A element in graph
graph["a"] = {}
graph["a"]["fin"] = 1

# Put B element in graph
graph["b"] = {}
graph["b"]["fin"] = 5
graph["b"]["a"] = 3

# Put last element in graph
graph["fin"] = {}

# Now we need to declare "costs" hash table
# Costs table shows how much time needed to reach to this node from the starting node

# Settings up infinity value
infinity = float("inf")

# initializing "costs" hash-table
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity

# We also needed "parents" hash-table
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

# ------- Notebook graph example -------

graph_notebook = {}

graph_notebook["a"] = {}
graph_notebook["a"]["b"] = 1
graph_notebook["a"]["d"] = 10

graph_notebook["b"] = {}
graph_notebook["b"]["c"] = 2

graph_notebook["c"] = {}
graph_notebook["c"]["d"] = 3
graph_notebook["c"]["e"] = 15

graph_notebook["d"] = {}
graph_notebook["d"]["e"] = 1

graph_notebook["e"] = {}

costs_notebook = {}
costs_notebook["b"] = 1
costs_notebook["d"] = 10
costs_notebook["c"] = infinity
costs_notebook["e"] = infinity

# We also needed "parents" hash-table
parents_notebook = {}
parents_notebook["b"] = "a"
parents_notebook["d"] = "a"
parents_notebook["c"] = None
parents_notebook["e"] = None


def dijkstra_algo(graph, costs, parents):
    # We need to store processed nodes list
    processed = []

    # First we need to define function for finding the lowest cost node to process it
    def find_lowest_cost_node(costs):
        lowest_cost = float("inf")
        lowest_cost_node = None
        for node in costs:
            cost = costs[node]
            if cost < lowest_cost and node not in processed:
                lowest_cost = cost
                lowest_cost_node = node
        return lowest_cost_node

    # And now we initiate the algorithm
    node = find_lowest_cost_node(costs)
    step = 1
    while node is not None:
        print(f"Step {step}")
        print(f"Current data:\nParents: {parents}\nCosts: {costs}\n\n")
        print(f"current lowest node: {node}. Start to inspect it")
        cost = costs[node]
        neighbors = graph[node]
        print(f"Cost of node {node} = {cost}")
        print(f"Neighbors of node {node} = {neighbors}")
        for n in neighbors.keys():
            print(f" Processing neighbor {n}")
            new_cost = cost + neighbors[n]
            print(f" New calculated cost for neighbor {n} = {new_cost}")
            if costs[n] > new_cost:
                print(
                    f"  New calculated cost {new_cost} is lower than old node cost {costs[n]} so we replace it with new value and new parent {node}"
                )
                costs[n] = new_cost
                parents[n] = node
                print(
                    f"  New costs table:\n  {costs}\n  New parents table:\n  {parents}"
                )
            else:
                print(
                    f"  New calculated cost {new_cost} is greater than old node cost {costs[n]} so we do not touching costs and parents table"
                )
                print(f"  Costs table:\n  {costs}\n  Parents table:\n  {parents}")
        print(
            f"Work with node {node} is finished, so we marking it with processed label"
        )
        processed.append(node)
        node = find_lowest_cost_node(costs)
        step += 1
        print("--------------------")

    print(processed)
    print(parents)
    print(costs)


dijkstra_algo(graph_notebook, costs_notebook, parents_notebook)
