/**
 * @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);
}
LONGBOW_TEST_CASE(Global, athenaTransportLinkAdapter_GetLogger)
{
    AthenaTransportLinkAdapter *athenaTransportLinkAdapter = athenaTransportLinkAdapter_Create(_removeLink, NULL);
    assertNotNull(athenaTransportLinkAdapter, "athenaTransportLinkAdapter_Create returned NULL");
    PARCLog *logger = athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter);
    assertNotNull(logger, "logger not setup for adapter");
    athenaTransportLinkAdapter_Destroy(&athenaTransportLinkAdapter);
}
int
athenaTransportLinkAdapter_Poll(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, int timeout)
{
    struct pollfd *pollfdReceiveList = athenaTransportLinkAdapter->pollfdReceiveList;
    struct pollfd *pollfdSendList = athenaTransportLinkAdapter->pollfdSendList;
    int pollfdListSize = athenaTransportLinkAdapter->pollfdListSize;
    AthenaTransportLinkModule *athenaTransportLinkModule;
    int events = 0;

    // Allow instances which have not registered an eventfd to mark their events
    if (athenaTransportLinkAdapter->moduleList) {
        for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->moduleList); index++) {
            athenaTransportLinkModule = parcArrayList_Get(athenaTransportLinkAdapter->moduleList, index);
            events += athenaTransportLinkModule_Poll(athenaTransportLinkModule, timeout);
        }
    }

    if (events) { // if we have existing events, poll doesn't need to block
        timeout = 0;
    }

    int result = poll(pollfdReceiveList, pollfdListSize, timeout);
    if (result < 0) {
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Receive list poll error: (%d) %s", errno, strerror(errno));
    } else {
        for (int index = 0; index < pollfdListSize; index++) {
            if (pollfdReceiveList[index].revents) {
                AthenaTransportLink *athenaTransportLink = athenaTransportLinkAdapter->pollfdTransportLink[index];
                if (athenaTransportLink) {
                    if (pollfdReceiveList[index].revents & (POLLERR | POLLHUP)) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
                        athenaTransportLink_Close(athenaTransportLink);
                    }
                    if (pollfdReceiveList[index].revents & POLLIN) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive);
                    } else {
                        athenaTransportLink_ClearEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive);
                    }
                }
            }
        }
    }

    result = poll(pollfdSendList, pollfdListSize, 0);
    if (result < 0) {
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Send list poll error: (%d) %s", errno, strerror(errno));
    } else {
        for (int index = 0; index < pollfdListSize; index++) {
            if (pollfdSendList[index].revents) {
                AthenaTransportLink *athenaTransportLink = athenaTransportLinkAdapter->pollfdTransportLink[index];
                if (athenaTransportLink) {
                    if (pollfdSendList[index].revents & (POLLNVAL | POLLHUP | POLLERR)) {
                        continue;
                    }
                    if (pollfdSendList[index].revents & (POLLERR | POLLHUP)) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
                        athenaTransportLink_Close(athenaTransportLink);
                    }
                    if (pollfdSendList[index].revents & POLLOUT) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);
                    } else {
                        athenaTransportLink_ClearEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);
                    }
                }
            }
        }
        //events += result; // don't register send events
    }
    return events;
}
static AthenaTransportLinkModule *
_LoadModule(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, const char *moduleName)
{
    assertTrue(_LookupModule(athenaTransportLinkAdapter, moduleName) == NULL,
               "attempt to load an already loaded module");

    // Derive the entry initialization name from the provided module name
    const char *moduleEntry;
    moduleEntry = _moduleNameToInitMethod(moduleName);

    // Check to see if the module was statically linked in.
    void *linkModule = RTLD_DEFAULT;
    ModuleInit _init = dlsym(linkModule, moduleEntry);

    // If not statically linked in, look for a shared library and load it from there
    if (_init == NULL) {
        // Derive the library name from the provided module name
        const char *moduleLibrary;
        moduleLibrary = _moduleNameToLibrary(moduleName);

        void *linkModule = dlopen(moduleLibrary, RTLD_NOW | RTLD_GLOBAL);
        parcMemory_Deallocate(&moduleLibrary);

        // If the shared library wasn't found, look for the symbol in our existing image.  This
        // allows a link module to be linked directly into Athena without modifying the forwarder.
        if (linkModule == NULL) {
            parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                          "Unable to dlopen %s: %s", moduleName, dlerror());
            parcMemory_Deallocate(&moduleEntry);
            errno = ENOENT;
            return NULL;
        }

        _init = dlsym(linkModule, moduleEntry);
        if (_init == NULL) {
            parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                          "Unable to find %s module _init method: %s", moduleName, dlerror());
            parcMemory_Deallocate(&moduleEntry);
            dlclose(linkModule);
            errno = ENOENT;
            return NULL;
        }
    }
    parcMemory_Deallocate(&moduleEntry);

    // Call the initialization method.
    PARCArrayList *moduleList = _init();
    if (moduleList == NULL) { // if the init method fails, unload the module if it was loaded
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Empty module list returned from %s module", moduleName);
        if (linkModule != RTLD_DEFAULT) {
            dlclose(linkModule);
        }
        errno = ENOENT;
        return NULL;
    }

    // Process each link module instance (typically only one)
    for (int index = 0; index < parcArrayList_Size(moduleList); index++) {
        AthenaTransportLinkModule *athenaTransportLinkModule = parcArrayList_Get(moduleList, index);
        _AddModule(athenaTransportLinkAdapter, athenaTransportLinkModule);
    }
    parcArrayList_Destroy(&moduleList);

    return _LookupModule(athenaTransportLinkAdapter, moduleName);
}