PARCBitVector *
athenaTransportLinkAdapter_Send(AthenaTransportLinkAdapter *athenaTransportLinkAdapter,
                                CCNxMetaMessage *ccnxMetaMessage,
                                PARCBitVector *linkOutputVector)
{
    PARCBitVector *resultVector = parcBitVector_Create();
    int nextLinkToWrite = 0;

    if (athenaTransportLinkAdapter->instanceList == NULL) {
        return resultVector;
    }

    while ((nextLinkToWrite = parcBitVector_NextBitSet(linkOutputVector, nextLinkToWrite)) >= 0) {
        athenaTransportLinkAdapter->stats.messageSend_Attempted++;
        if (nextLinkToWrite >= parcArrayList_Size(athenaTransportLinkAdapter->instanceList)) {
            athenaTransportLinkAdapter->stats.messageSend_LinkDoesNotExist++;
            nextLinkToWrite++;
            continue;
        }
        AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->instanceList, nextLinkToWrite);
        if (athenaTransportLink == NULL) {
            athenaTransportLinkAdapter->stats.messageSend_LinkDoesNotExist++;
            nextLinkToWrite++;
            continue;
        }
        if (!(athenaTransportLink_GetEvent(athenaTransportLink) & AthenaTransportLinkEvent_Send)) {
            athenaTransportLinkAdapter->stats.messageSend_LinkNotAcceptingSendRequests++;
            nextLinkToWrite++;
            continue;
        }
        // If we're sending an interest to a non-local link,
        // check that it has a sufficient hoplimit.
        if (ccnxMetaMessage_IsInterest(ccnxMetaMessage)) {
            if (athenaTransportLink_IsNotLocal(athenaTransportLink)) {
                if (ccnxInterest_GetHopLimit(ccnxMetaMessage) == 0) {
                    athenaTransportLinkAdapter->stats.messageSend_HopLimitExceeded++;
                    nextLinkToWrite++;
                    continue;
                }
            }
        }
        int result = athenaTransportLink_Send(athenaTransportLink, ccnxMetaMessage);
        if (result == 0) {
            athenaTransportLinkAdapter->stats.messageSent++;
            parcBitVector_Set(resultVector, nextLinkToWrite);
        } else {
            athenaTransportLinkAdapter->stats.messageSend_LinkSendFailed++;
        }
        nextLinkToWrite++;
    }

    return resultVector;
}
bool
athenaTransportLinkAdapter_IsNotLocal(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, int linkId)
{
    if (athenaTransportLinkAdapter->instanceList == NULL) {
        return false;
    }
    if ((linkId >= 0) && (linkId < parcArrayList_Size(athenaTransportLinkAdapter->instanceList))) {
        AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->instanceList, linkId);
        if (athenaTransportLink) {
            return athenaTransportLink_IsNotLocal(athenaTransportLink);
        }
    }
    return false;
}
static AthenaTransportLink *
_newLink(AthenaTransportLink *athenaTransportLink, _UDPLinkData *newLinkData)
{
    // Accept a new tunnel connection.

    // Clone a new link from the current listener.
    const char *derivedLinkName = _createNameFromLinkData(&newLinkData->link);
    AthenaTransportLink *newTransportLink = athenaTransportLink_Clone(athenaTransportLink,
                                                                      derivedLinkName,
                                                                      _UDPSend,
                                                                      _UDPReceiveProxy,
                                                                      _UDPClose);
    if (newTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Clone failed");
        parcMemory_Deallocate(&derivedLinkName);
        _UDPLinkData_Destroy(&newLinkData);
        return NULL;
    }

    _setConnectLinkState(newTransportLink, newLinkData);

    // Send the new link up to be added.
    int result = athenaTransportLink_AddLink(athenaTransportLink, newTransportLink);
    if (result == -1) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_AddLink failed: %s", strerror(errno));
        _UDPLinkData_Destroy(&newLinkData);
        athenaTransportLink_Release(&newTransportLink);
    } else {
        parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                     "new link accepted by %s: %s %s",
                     athenaTransportLink_GetName(athenaTransportLink), derivedLinkName,
                     athenaTransportLink_IsNotLocal(athenaTransportLink) ? "" : "(Local)");
    }

    parcMemory_Deallocate(&derivedLinkName);

    // Could pass a message back here regarding the new link.
    return newTransportLink;
}
static CCNxMetaMessage *
_create_linkList_response(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, CCNxName *ccnxName)
{
    PARCJSONArray *jsonLinkList = parcJSONArray_Create();

    for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->listenerList); index++) {
        AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->listenerList, index);
        const char *linkName = athenaTransportLink_GetName(athenaTransportLink);
        bool notLocal = athenaTransportLink_IsNotLocal(athenaTransportLink);
        bool localForced = athenaTransportLink_IsForceLocal(athenaTransportLink);
        PARCJSON *jsonItem = parcJSON_Create();
        parcJSON_AddString(jsonItem, "linkName", linkName);
        parcJSON_AddInteger(jsonItem, "index", -1);
        parcJSON_AddBoolean(jsonItem, "notLocal", notLocal);
        parcJSON_AddBoolean(jsonItem, "localForced", localForced);

        PARCJSONValue *jsonItemValue = parcJSONValue_CreateFromJSON(jsonItem);
        parcJSON_Release(&jsonItem);

        parcJSONArray_AddValue(jsonLinkList, jsonItemValue);
        parcJSONValue_Release(&jsonItemValue);

        if (notLocal) {
            parcLog_Debug(athenaTransportLinkAdapter->log, "\n    Link listener%s: %s", localForced ? " (forced remote)" : "", linkName);
        } else {
            parcLog_Debug(athenaTransportLinkAdapter->log, "\n    Link listener%s: %s", localForced ? " (forced local)" : "", linkName);
        }
    }
    for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->instanceList); index++) {
        AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->instanceList, index);
        if (athenaTransportLink) {
            const char *linkName = athenaTransportLink_GetName(athenaTransportLink);
            bool notLocal = athenaTransportLink_IsNotLocal(athenaTransportLink);
            bool localForced = athenaTransportLink_IsForceLocal(athenaTransportLink);
            PARCJSON *jsonItem = parcJSON_Create();
            parcJSON_AddString(jsonItem, "linkName", linkName);
            parcJSON_AddInteger(jsonItem, "index", index);
            parcJSON_AddBoolean(jsonItem, "notLocal", notLocal);
            parcJSON_AddBoolean(jsonItem, "localForced", localForced);

            PARCJSONValue *jsonItemValue = parcJSONValue_CreateFromJSON(jsonItem);
            parcJSON_Release(&jsonItem);

            parcJSONArray_AddValue(jsonLinkList, jsonItemValue);
            parcJSONValue_Release(&jsonItemValue);

            if (notLocal) {
                parcLog_Debug(athenaTransportLinkAdapter->log, "\n    Link instance [%d] %s: %s", index, localForced ? "(forced remote)" : "(remote)", linkName);
            } else {
                parcLog_Debug(athenaTransportLinkAdapter->log, "\n    Link instance [%d] %s: %s", index, localForced ? "(forced local)" : "(local)", linkName);
            }
        }
    }

    char *jsonString = parcJSONArray_ToString(jsonLinkList);

    parcJSONArray_Release(&jsonLinkList);

    PARCBuffer *payload = parcBuffer_CreateFromArray(jsonString, strlen(jsonString));

    CCNxContentObject *contentObject =
        ccnxContentObject_CreateWithDataPayload(ccnxName, parcBuffer_Flip(payload));

    struct timeval tv;
    gettimeofday(&tv, NULL);
    uint64_t nowInMillis = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    ccnxContentObject_SetExpiryTime(contentObject, nowInMillis + 100); // this response is good for 100 millis

    CCNxMetaMessage *result = ccnxMetaMessage_CreateFromContentObject(contentObject);

    ccnxContentObject_Release(&contentObject);
    parcBuffer_Release(&payload);
    parcMemory_Deallocate(&jsonString);

    athena_EncodeMessage(result);
    return result;
}