/**
 * @abstract add a new link instance to the AthenaTransportLinkAdapter instance list
 * @discussion
 *
 * When athenaTransportLinkAdapter_Open is called on a link specific module, the module instantiates
 * the link and then creates a new AthenaTransportLink to interface with the AthenaTransportLinkAdapter.
 * The AthenaTransportLinkModule passes the new AthenaTransportLink to the AthenaTransportLinkAdapter by
 * calling AthenaTransportLinkAdapter_AddLink with the new link AthenaTransportLink data. The AthenaTransportLinkAdapter
 * then places the new link instance on its internal instance list in a pending state until it has been verified and ensures
 * that the new link doesn't collide (i.e. by name) with any currently registered link before returning success.
 *
 * New routable links are placed into instanceList slots that have previously been vacated before being added
 * to the end of the instanceList.  This is in order to keep bit vectors that are based on the instanceList as
 * small as is necessary.
 *
 * @param [in] athenaTransportLinkAdapter link adapter instance
 * @param [in] linkInstance instance structure created by the link specific module
 * @return 0 on success, -1 with errno set to indicate the error
 *
 * Example:
 * @code
 * {
 * }
 * @endcode
 */
static int
_athenaTransportLinkAdapter_AddLink(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, AthenaTransportLink *newTransportLink)
{
    int linkId = -1;

    // Check for existing linkName in listenerList
    if (athenaTransportLinkAdapter->listenerList) {
        for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->listenerList); index++) {
            AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->listenerList, index);
            if (strcmp(athenaTransportLink_GetName(athenaTransportLink), athenaTransportLink_GetName(newTransportLink)) == 0) {
                errno = EADDRINUSE; // name is already in listenerList
                return -1;
            }
        }
    }

    // Check for existing linkName in the instanceList
    if (athenaTransportLinkAdapter->instanceList) {
        for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->instanceList); index++) {
            AthenaTransportLink *athenaTransportLink = parcArrayList_Get(athenaTransportLinkAdapter->instanceList, index);
            if (athenaTransportLink) {
                if (strcmp(athenaTransportLink_GetName(athenaTransportLink), athenaTransportLink_GetName(newTransportLink)) == 0) {
                    errno = EADDRINUSE; // name is already in instanceList
                    return -1;
                }
            } else {
                // remember the first available index found along the way
                if (linkId == -1) {
                    linkId = index;
                }
            }
        }
    }

    // Add to listenerList or instanceList
    athenaTransportLink_Acquire(newTransportLink);
    if (athenaTransportLink_IsNotRoutable(newTransportLink)) { // listener
        bool result = parcArrayList_Add(athenaTransportLinkAdapter->listenerList, newTransportLink);
        assertTrue(result, "parcArrayList_Add failed to add new listener");
    } else { // routable link, add to instances using the last available id if one was seen
        if (linkId != -1) {
            parcArrayList_Set(athenaTransportLinkAdapter->instanceList, linkId, newTransportLink);
        } else {
            bool result = parcArrayList_Add(athenaTransportLinkAdapter->instanceList, newTransportLink);
            assertTrue(result, "parcArrayList_Add failed to add new link instance");
        }
    }

    // If any transport link has a registered file descriptor add it to the general polling list.
    int eventFd = athenaTransportLink_GetEventFd(newTransportLink);
    if (eventFd != -1) {
        _add_to_pollfdList(athenaTransportLinkAdapter, newTransportLink, eventFd);
    }
    return 0;
}
/**
 * @abstract called from below the link adapter to coordinate termination of a link instance
 * @discussion
 *
 * This is called exclusively from the Transport Link Module to instigate the removal of
 * an active link.  The link has been flagged closing by the originator.  This method
 * must ensure that all references to the link have been removed and then call the
 * instance close method to finish the operation.
 *
 * @param [in] athenaTransportLinkAdapter link adapter instance
 * @param [in] athenaTransportLink link instance to remove
 *
 * Example:
 * @code
 * {
 *
 * }
 * @endcode
 */
static void
_athenaTransportLinkAdapter_RemoveLink(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, AthenaTransportLink *athenaTransportLink)
{
    int linkId = -1;

    // if this is a listener it can simply be removed
    if (athenaTransportLink_IsNotRoutable(athenaTransportLink)) {
        if (athenaTransportLinkAdapter->listenerList) {
            for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->listenerList); index++) {
                AthenaTransportLink *transportLink = parcArrayList_Get(athenaTransportLinkAdapter->listenerList, index);
                if (athenaTransportLink == transportLink) {
                    parcArrayList_RemoveAtIndex(athenaTransportLinkAdapter->listenerList, index);
                    _remove_from_pollfdList(athenaTransportLinkAdapter, athenaTransportLink);
                    parcLog_Debug(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter), "listener removed: %s",
                                  athenaTransportLink_GetName(athenaTransportLink));
                    athenaTransportLink_Release(&athenaTransportLink);
                    return;
                }
            }
        }
    }

    // Remove from our internal instance list.
    // The index entry remains to be reused by links that are added in the future.
    if (athenaTransportLinkAdapter->instanceList) {
        for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->instanceList); index++) {
            AthenaTransportLink *transportLink = parcArrayList_Get(athenaTransportLinkAdapter->instanceList, index);
            if (athenaTransportLink == transportLink) {
                parcArrayList_Set(athenaTransportLinkAdapter->instanceList, index, NULL);
                _remove_from_pollfdList(athenaTransportLinkAdapter, athenaTransportLink);
                linkId = index;
                break;
            }
        }
    }

    assertFalse(linkId == -1, "Attempt to remove link not found in link adapter lists");

    // Callback to notify that the link has been removed and references need to be dropped.
    PARCBitVector *linkVector = parcBitVector_Create();
    parcBitVector_Set(linkVector, linkId);
    athenaTransportLinkAdapter->removeLink(athenaTransportLinkAdapter->removeLinkContext, linkVector);
    parcBitVector_Release(&linkVector);

    // we assume all references to the linkId associated with this instance have been
    // cleared from the PIT and FIB when removeLink returns.

    parcLog_Debug(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                  "link removed: %s", athenaTransportLink_GetName(athenaTransportLink));

    athenaTransportLink_Release(&athenaTransportLink);
}