#include "fastfetch.h"
#include "common/library.h"

#ifndef FF_DISABLE_DLOPEN

#include <stdarg.h>

//Clang doesn't define __SANITIZE_ADDRESS__ but defines __has_feature(address_sanitizer)
#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature)
    #if __has_feature(address_sanitizer)
        #define __SANITIZE_ADDRESS__
    #endif
#endif

#ifndef FF_DLOPEN_FLAGS
    #ifdef __SANITIZE_ADDRESS__
        #define FF_DLOPEN_FLAGS RTLD_LAZY | RTLD_NODELETE
    #else
        #define FF_DLOPEN_FLAGS RTLD_LAZY
    #endif
#endif

static void* libraryLoad(const char* path, int maxVersion)
{
    void* result = dlopen(path, FF_DLOPEN_FLAGS);

    #ifdef _WIN32

    // libX.dll.1 never exists on Windows, while libX-1.dll may exist
    FF_UNUSED(maxVersion)

    if(result != NULL)
        return result;

    uint32_t pathLen = ffStrbufLastIndexC(&instance.state.platform.exePath, '/');
    if (pathLen == instance.state.platform.exePath.length)
        return result;

    char absPath[MAX_PATH + 1];
    strcpy(mempcpy(absPath, instance.state.platform.exePath.chars, pathLen + 1), path);
    return dlopen(absPath, FF_DLOPEN_FLAGS);

    #else

    if(result != NULL || maxVersion < 0)
        return result;

    FF_STRBUF_AUTO_DESTROY pathbuf = ffStrbufCreateA(64);
    ffStrbufAppendS(&pathbuf, path);
    ffStrbufAppendC(&pathbuf, '.');

    for(int i = maxVersion; i >= 0; --i)
    {
        uint32_t originalLength = pathbuf.length;
        ffStrbufAppendF(&pathbuf, "%i", i);

        result = dlopen(pathbuf.chars, FF_DLOPEN_FLAGS);
        if(result != NULL)
            break;

        ffStrbufSubstrBefore(&pathbuf, originalLength);
    }

    #endif

    return result;
}

void* ffLibraryLoad(const char* path, int maxVersion, ...)
{
    void* result = libraryLoad(path, maxVersion);

    if (!result)
    {
        va_list defaultNames;
        va_start(defaultNames, maxVersion);

        do
        {
            const char* pathRest = va_arg(defaultNames, const char*);
            if(pathRest == NULL)
                break;

            int maxVersionRest = va_arg(defaultNames, int);
            result = libraryLoad(pathRest, maxVersionRest);
        } while (!result);

        va_end(defaultNames);
    }

    return result;
}

#endif
