netloc_node_t * netloc_dc_get_node_by_physical_id(netloc_data_collection_handle_t *handle, char * phy_id)
{
    unsigned long key_int;

    if( NULL == phy_id ) {
        return NULL;
    }

    SUPPORT_CONVERT_ADDR_TO_INT(phy_id, handle->network->network_type, key_int);
    return netloc_lookup_table_access_with_int( handle->node_list, phy_id, key_int );
}
/*************************************************************
 * Support Functionality
 *************************************************************/
static int compute_shortest_path_dijkstra(netloc_data_collection_handle_t *handle,
                                          netloc_node_t *src_node,
                                          netloc_node_t *dest_node,
                                          int *num_edges,
                                          netloc_edge_t ***edges)
{
    int exit_status = NETLOC_SUCCESS;
    int i;
    pq_queue_t *queue = NULL;
    int *distance = NULL;
    bool *not_seen = NULL;
    netloc_node_t *node_u = NULL;
    netloc_node_t *node_v = NULL;
    netloc_node_t **prev_node = NULL;
    netloc_edge_t **prev_edge = NULL;
    int alt;
    int idx_u, idx_v;

    int num_rev_edges;
    netloc_edge_t **rev_edges = NULL;

    struct netloc_dt_lookup_table_iterator *hti = NULL;
    netloc_node_t *cur_node = NULL;

    unsigned long key_int;

    // Just in case things go poorly below
    (*num_edges) = 0;
    (*edges) = NULL;


    /*
     * Allocate some data structures
     */
    queue = pq_queue_t_construct();
    if( NULL == queue ) {
        fprintf(stderr, "Error: Failed to allocate the queue\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    distance = (int*)malloc(sizeof(int) * netloc_lookup_table_size(handle->node_list));
    if( NULL == distance ) {
        fprintf(stderr, "Error: Failed to allocate the distance array\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    not_seen = (bool*)malloc(sizeof(bool) * netloc_lookup_table_size(handle->node_list));
    if( NULL == not_seen ) {
        fprintf(stderr, "Error: Failed to allocate the 'not_seen' array\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    prev_node = (netloc_node_t**)malloc(sizeof(netloc_node_t*) * netloc_lookup_table_size(handle->node_list));
    if( NULL == prev_node ) {
        fprintf(stderr, "Error: Failed to allocate the 'prev_node' array\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    prev_edge = (netloc_edge_t**)malloc(sizeof(netloc_edge_t*) * netloc_lookup_table_size(handle->node_list));
    if( NULL == prev_edge ) {
        fprintf(stderr, "Error: Failed to allocate the 'prev_edge' array\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    /*
     * Initialize the data structures
     */
    // Make sure to initialize the arrays
    for( i = 0; i < netloc_lookup_table_size(handle->node_list); ++i){
        distance[i] = INT_MAX;
        not_seen[i] = true;
        prev_node[i] = NULL;
        prev_edge[i] = NULL;
    }

    i = 0;
    hti = netloc_dt_lookup_table_iterator_t_construct(handle->node_list);
    while( !netloc_lookup_table_iterator_at_end(hti) ) {
        cur_node = (netloc_node_t*)netloc_lookup_table_iterator_next_entry(hti);
        if( NULL == cur_node ) {
            break;
        }

        if( cur_node == src_node ) {
            pq_push(queue, 0, cur_node);
            distance[i] = 0;
        } else {
            pq_push(queue, INT_MAX, cur_node);
            distance[i] = INT_MAX;
        }

        not_seen[i] = true;

        prev_node[i] = NULL;
        prev_edge[i] = NULL;

        cur_node->__uid__ = i;
        ++i;
    }

    /*
     * Search
     */
    while( !pq_is_empty(queue) ) {
        //pq_dump(queue);

        // Grab the next hop
        node_u = pq_pop(queue);
        // Mark as seen
        idx_u = -1;
        i = 0;
        idx_u = node_u->__uid__;
        not_seen[idx_u] = false;

        // For all the edges from this node
        for(i = 0; i < node_u->num_edges; ++i ) {
            node_v = NULL;
            idx_v  = -1;

            // Lookup the "dest" node
            node_v = node_u->edges[i]->dest_node;
            idx_v = node_v->__uid__;

            // If the node has been seen, skip
            if( !not_seen[idx_v] ) {
                continue;
            }

            // Otherwise check to see if we found a shorter path
            // Future Work: Add a weight factor other than 1.
            //              Maybe calculated based on speed/width
            alt = distance[idx_u] + 1;
            if( alt < distance[idx_v] ) {
                distance[idx_v] = alt;
                prev_node[idx_v] = node_u;
                prev_edge[idx_v] = node_u->edges[i];

                // Adjust the priority queue as needed
                pq_reorder(queue, alt, node_v);
            }
        }
    }

    /*
     * Reconstruct the path by picking up the edges
     * The edges will be in reverse order (dest to source).
     */
    num_rev_edges = 0;
    rev_edges     = NULL;

    // Find last hop
    SUPPORT_CONVERT_ADDR_TO_INT(dest_node->physical_id,
                                handle->network->network_type,
                                key_int);
    node_u  = netloc_lookup_table_access_with_int( handle->node_list,
                                                   dest_node->physical_id,
                                                   key_int);
    idx_u = node_u->__uid__;

    node_v = NULL;
    idx_v  = -1;
    while( prev_node[idx_u] != NULL ) {
        // Find the linking edge
        if( node_u != dest_node) {
            for(i = 0; i < node_u->num_edges; ++i ) {
                if( node_v->physical_id_int == node_u->edges[i]->dest_node->physical_id_int ) {
                    ++num_rev_edges;
                    rev_edges = (netloc_edge_t**)realloc(rev_edges, sizeof(netloc_edge_t*) * num_rev_edges);
                    if( NULL == rev_edges ) {
                        fprintf(stderr, "Error: Failed to re-allocate the 'rev_edges' array with %d elements\n",
                                num_rev_edges);
                        exit_status = NETLOC_ERROR;
                        goto cleanup;
                    }
                    rev_edges[num_rev_edges-1] = node_u->edges[i];
                    break;
                }
            }
        }
        node_v = node_u;
        idx_v  = idx_u;

        // Find the next node
        SUPPORT_CONVERT_ADDR_TO_INT(prev_node[idx_u]->physical_id,
                                    handle->network->network_type,
                                    key_int);
        node_u = netloc_lookup_table_access_with_int( handle->node_list,
                                                      prev_node[idx_u]->physical_id,
                                                      key_int);
        idx_u = node_u->__uid__;
    }

    for(i = 0; i < src_node->num_edges; ++i ) {
        if( NULL == node_v ) {
            fprintf(stderr, "Error: This should never happen, but node_v is NULL at line %d in file %s\n",
                    __LINE__, __FILE__);
            exit_status = NETLOC_ERROR;
            goto cleanup;
        }
        if( node_v->physical_id_int == src_node->edges[i]->dest_node->physical_id_int ) {
            ++num_rev_edges;
            rev_edges = (netloc_edge_t**)realloc(rev_edges, sizeof(netloc_edge_t*) * num_rev_edges);
            if( NULL == rev_edges ) {
                fprintf(stderr, "Error: Failed to re-allocate the 'rev_edges' array with %d elements\n",
                        num_rev_edges);
                exit_status = NETLOC_ERROR;
                goto cleanup;
            }
            rev_edges[num_rev_edges-1] = node_u->edges[i];

            break;
        }
    }


    /*
     * Copy the edges back in correct order
     */
    (*num_edges) = num_rev_edges;
    (*edges) = (netloc_edge_t**)malloc(sizeof(netloc_edge_t*) * (*num_edges));
    if( NULL == (*edges) ) {
        fprintf(stderr, "Error: Failed to allocate the edges array\n");
        exit_status = NETLOC_ERROR;
        goto cleanup;
    }

    for( i = 0; i < num_rev_edges; ++i ) {
        (*edges)[i] = rev_edges[num_rev_edges-1-i];
        //printf("DEBUG: \t Edge: %s\n", netloc_pretty_print_edge_t( (*edges)[i] ) );
    }


    /*
     * Cleanup
     */
 cleanup:
    if( NULL != queue ) {
        pq_queue_t_destruct(queue);
        queue = NULL;
    }

    if( NULL != rev_edges ) {
        free(rev_edges);
        rev_edges = NULL;
    }

    if( NULL != distance ) {
        free(distance);
        distance = NULL;
    }

    if( NULL != not_seen ) {
        free(not_seen);
        not_seen = NULL;
    }

    if( NULL != prev_node ) {
        free(prev_node);
        prev_node = NULL;
    }

    if( NULL != prev_edge ) {
        free(prev_edge);
        prev_edge = NULL;
    }

    netloc_dt_lookup_table_iterator_t_destruct(hti);

    return exit_status;
}
int netloc_dc_append_edge_to_node(netloc_data_collection_handle_t *handle, netloc_node_t *node, netloc_edge_t *edge)
{
    char *key = NULL;
    netloc_edge_t *found_edge = NULL;
    netloc_node_t *found_node = NULL;
    unsigned long key_int;
    bool is_cached = false;

    /*
     * Setup the table for the first edge
     */
    if( NULL == handle->edges ) {
        handle->edges = calloc(1, sizeof(*handle->edges));
        netloc_lookup_table_init(handle->edges, 1, 0);
    }

    /*
     * Setup the table for the first node
     */
    if(NULL == handle->node_list) {
        handle->node_list = calloc(1, sizeof(*handle->node_list));
        netloc_lookup_table_init(handle->node_list, 1, 0);
    }

    /*
     * Check to see if we have seen this edge before
     */
    asprintf(&key, "%d", edge->edge_uid);
    found_edge = (netloc_edge_t*)netloc_lookup_table_access(handle->edges, key);
    free(key);
    key = NULL;
    // JJH: Should we be checking the contents of the edge, not just the key?

    /*
     * If not add it to the edges lookup table
     */
    if( NULL == found_edge ) {
        found_edge = netloc_dt_edge_t_dup(edge);

        asprintf(&key, "%d", found_edge->edge_uid);
        netloc_lookup_table_append(handle->edges, key, found_edge);
        free(key);
        key = NULL;
    }

    /*
     * Update the edge links
     * if the node endpoint is not in the list, then add a stub
     */
    if( NULL == edge->src_node_id ) {
        return NETLOC_ERROR_NOT_FOUND;
    }
    SUPPORT_CONVERT_ADDR_TO_INT(edge->src_node_id, handle->network->network_type, key_int);
    asprintf(&key, "%s", edge->src_node_id);
    found_node = netloc_lookup_table_access_with_int(handle->node_list, key, key_int);

    if( NULL == found_node ) {
        found_node = netloc_dt_node_t_construct();

        found_node->physical_id = strdup(edge->src_node_id);
        found_node->physical_id_int = key_int;
        found_node->node_type = NETLOC_NODE_TYPE_INVALID;

        netloc_lookup_table_append_with_int(handle->node_list, key, key_int, found_node);
    } else {
        is_cached = true;
    }
    found_edge->src_node = found_node;
    free(key);
    key = NULL;

    if( NULL == edge->dest_node_id ) {
        return NETLOC_ERROR_NOT_FOUND;
    }
    asprintf(&key, "%s", edge->dest_node_id);
    SUPPORT_CONVERT_ADDR_TO_INT(edge->dest_node_id, handle->network->network_type, key_int);
    found_node = netloc_lookup_table_access_with_int(handle->node_list, key, key_int);

    if( NULL == found_node ) {
        found_node = netloc_dt_node_t_construct();

        found_node->physical_id = strdup(edge->dest_node_id);
        found_node->physical_id_int = key_int;
        found_node->node_type = NETLOC_NODE_TYPE_INVALID;

        netloc_lookup_table_append_with_int(handle->node_list, key, key_int, found_node);
    }
    found_edge->dest_node = found_node;
    free(key);
    key = NULL;

    /*
     * Add the edge index to the node passed to us
     */
    node->num_edge_ids++;
    node->edge_ids = (int*)realloc(node->edge_ids, sizeof(int) * node->num_edge_ids);
    node->edge_ids[node->num_edge_ids -1] = found_edge->edge_uid;

    node->num_edges++;
    node->edges = (netloc_edge_t**)realloc(node->edges, sizeof(netloc_edge_t*) * node->num_edges);
    node->edges[node->num_edges -1] = found_edge;

    /*
     * Update the cached version of this node
     */
    if( is_cached ) {
        json_object_del(handle->node_data_acc, node->physical_id);
        json_object_set_new(handle->node_data_acc, node->physical_id, netloc_dt_node_t_json_encode(node));
    }

    return NETLOC_SUCCESS;
}
int netloc_dc_append_path(netloc_data_collection_handle_t *handle,
                          const char * src_node_id,
                          const char * dest_node_id,
                          int num_edges, netloc_edge_t **edges,
                          bool is_logical)
{
    int i;
    netloc_node_t *node = NULL;
    int *edge_ids = NULL;
    unsigned long key_int;

    /*
     * Find the source Node
     */
    SUPPORT_CONVERT_ADDR_TO_INT(src_node_id, handle->network->network_type, key_int);
    node = netloc_lookup_table_access_with_int( handle->node_list, src_node_id, key_int );

    if( NULL == node ) {
        fprintf(stderr, "Error: node not found in the list (id = %s)\n", src_node_id);
        return NETLOC_ERROR;
    }

    /*
     * Copy the edge id's
     * Note: The 'path' storage is not a set of edge pointers, but edge ids.
     * It is assumed that the user will push paths into the data collection, but
     * not have to retrieve that path from the dc_handle.
     */
    edge_ids = (int*)malloc(sizeof(int)* (num_edges+1));
    if( NULL == edge_ids ) {
        return NETLOC_ERROR;
    }

    for(i = 0; i < num_edges; ++i) {
        if( NULL == edges[i] ) {
            edge_ids[i] = NETLOC_EDGE_UID_INVALID;
        } else {
            edge_ids[i] = edges[i]->edge_uid;
        }
    }
    // Null terminated array
    edge_ids[num_edges] = NETLOC_EDGE_UID_NULL;


    /*
     * Add the entry to the hash
     */
    if( is_logical ) {
        if( NULL == node->logical_paths ) {
	    node->logical_paths = calloc(1, sizeof(*node->logical_paths));
            netloc_lookup_table_init(node->logical_paths, 1, 0);
            node->num_log_paths = 0;
        }
        node->num_log_paths += 1;
        netloc_lookup_table_append( node->logical_paths, dest_node_id, edge_ids);
    } else {
        if( NULL == node->physical_paths ) {
	    node->physical_paths = calloc(1, sizeof(*node->physical_paths));
            netloc_lookup_table_init(node->physical_paths, 1, 0);
            node->num_log_paths = 0;
        }
        node->num_log_paths += 1;
        netloc_lookup_table_append( node->physical_paths, dest_node_id, edge_ids);
    }


    return NETLOC_SUCCESS;
}
int netloc_dc_append_node(netloc_data_collection_handle_t *handle, netloc_node_t *node)
{
    char *key = NULL;
    netloc_node_t *cur_node = NULL;
    unsigned long key_int;

    /*
     * Setup the table for the first node
     */
    if(NULL == handle->node_list) {
        handle->node_list = calloc(1, sizeof(*handle->node_list));
        netloc_lookup_table_init(handle->node_list, 1, 0);
    }

    /*
     * Check to see if we have seen this node before
     */
    SUPPORT_CONVERT_ADDR_TO_INT(node->physical_id, handle->network->network_type, key_int);
    asprintf(&key, "%s", node->physical_id);
    cur_node = netloc_lookup_table_access_with_int(handle->node_list, key, key_int);

    if( NULL != cur_node ) {
        if( NETLOC_NODE_TYPE_INVALID == cur_node->node_type ) {
            // JJH: We should be able to use 'replace' instead of 'remove' and 'append'
            //      Need to double check ordering.
            netloc_lookup_table_remove_with_int(handle->node_list, key, key_int);
        } else {
            fprintf(stderr, "Warning: A version of this node has already been added to the data set!\n");
            fprintf(stderr, "Warning: Support for updating nodes is not yet available\n");
            // JJH: Delete the old cached version ? replace it?
            //json_object_del(handle->node_data_acc, node->physical_id);
        }
    }
    free(key);
    key = NULL;

    /*
     * Add the node to our list
     */
    if( NULL == cur_node ) {
        cur_node = netloc_dt_node_t_dup(node);

        cur_node->__uid__ = 0;

        cur_node->physical_id_int = key_int;
        asprintf(&key, "%s", node->physical_id);
        netloc_lookup_table_append_with_int(handle->node_list, key, key_int, cur_node);

        /*
         * Encode the data: Physical ID is the key
         */
        json_object_set_new(handle->node_data_acc, node->physical_id, netloc_dt_node_t_json_encode(node));
    }
    else if( NETLOC_NODE_TYPE_INVALID == cur_node->node_type ) {
        netloc_dt_node_t_copy(node, cur_node);

        cur_node->__uid__ = 0;

        cur_node->physical_id_int = key_int;
        asprintf(&key, "%s", node->physical_id);
        netloc_lookup_table_append_with_int(handle->node_list, key, key_int, cur_node);

        /*
         * Encode the data: Physical ID is the key
         */
        json_object_set_new(handle->node_data_acc, node->physical_id, netloc_dt_node_t_json_encode(node));
    }
    else {
        /*
         * Replace the node?
         * The code below will fix the cache (node_data_acc), but the handle->node_list needs to also be updated.
         */
        //json_object_del(handle->node_data_acc, node->physical_id);
        //json_object_set_new(handle->node_data_acc, node->physical_id, netloc_dt_node_t_json_encode(node));
        return NETLOC_ERROR_NOT_IMPL;
    }

    return NETLOC_SUCCESS;
}