from noderank import Noderank

class VNR:
    def __init__(self):
        self.nodes = {} # vNodeID: [pNode, cpu]
        self.links = [] # List of list, []

class Path:
    def __init__(self):
        self.path = []
        self.weight = 0

    def copy(self):
        p = Path()
        p.weight = self.weight
        for i in self.path:
            p.path.append(i)
        return p


def load_vn(source):
    return Noderank(source)

def getVNR(network, source):
    n = Noderank(source)


def node_map(network, request):
    pairing = {} # key: vNodeID, value: nodeID

    for vnr_node_id in range(len(request.node_cpu)):
        #print("Processing VRN node rew of CPU {}".format(request.node_cpu[vnr_node_id]))
        for rank_id in range(len(network.ranks)):
            node = network.ranks[rank_id]
            tmp_node_cpu = network.node_cpu[node]
            # subtract assigned capacity - only commit if placement is possible
            for k, v in pairing.items():
                if v == node:
                    tmp_node_cpu -= request.node_cpu[k]
            print("    {} Available: {}".format(node, tmp_node_cpu))
            if  tmp_node_cpu >= request.node_cpu[vnr_node_id]:
                print("VRN node {} placed on node {}".format(vnr_node_id, node))
                pairing[vnr_node_id] = node
                #network.node_cpu[node] -= request.node_cpu[vnr_node_id]
                break
    if len(pairing) == len(request.node_cpu):
        vnr = VNR()
        for v, p in pairing.items():
            vnr.nodes[v] = [p, request.node_cpu[v]]
        return vnr
    return None

def min_index(array, Q):
    max_val = -1
    max_ind = None
    for i in range(len(array)):
        if Q[i] is not None:
            if array[i] > max_val:
                max_val = array[i]
                max_ind = i
    return max_ind

def all_none(array):
    for i in array:
        if i is not None:
            return False
    return True


# data - list of lists, each item contains the next hops for a given index
# s - int index, current source node for one iteration
def make_paths(data, paths):
    new_paths = []
    expanded = False
    for p in paths:
        p_end = p[-1]
        for i in data[p[-1]]:
            #print(i)
            temp_path = []
            temp_path.extend(p)
            if i not in temp_path:                
                temp_path.append(i)
                expanded = True
                new_paths.append(temp_path)
    
    #if expanded:
    if len(new_paths) > 0:
        new_paths.extend(make_paths(data, new_paths))
        return new_paths
    else:
        return new_paths
    


# network - Noderank
# source - int index
# dest - int index
def path_finder(network, source, dest):
    # Dijkstra
    Q = []
    dist = []
    prev = []
    for i in range(len(network.node_matrix)):
        dist.append(9999999999)
        prev.append([])
        Q.append(i)
    dist[source] = 0

    while not all_none(Q) > 0:
        u = min_index(dist, Q)
        Q[u] = None
        #print(Q)
        #print(u)

        for v in range(len(network.node_matrix)):
            if network.node_matrix[u][v] > 0: # if U and V are neighbours
                prev[v].append(u)

        if u == dest:
            break
    # Paths from next hops
    paths = []
    for p in make_paths(prev, [[source]]):
        if p[0] == source and p[-1] == dest:
            path = Path()
            path.path = p
            for i in range(len(p)-1):
                path.weight += network.node_matrix[p[i]][p[i+1]]
            paths.append(path)
            #print("{0}: {1}".format(path.path, path.weight))
    paths.sort(key=lambda p: p.weight)
    return paths




def nw_map(network, vnr, request):
    # k shortest path between vnr assigned nodes
    pass


if __name__ == "__main__":
    network = Noderank("VNE7.txt")
    print(network.ranks)
    request = Noderank("VNR1.txt")
    paths = path_finder(network, 0, 3)
    for path in paths:
        print("{0}: {1}".format(path.path, path.weight))
    exit(0)
    vnr = node_map(network, request)
    if vnr is None:
        print("Can't assign VNR1! Not enough CPU.")
    else:
        if nw_map:
            pass
        else:
            print("Can't assign VNR1! Not enough network.")