#ifndef BLUESPY_LOADER_INCLUDE_GUARD
#define BLUESPY_LOADER_INCLUDE_GUARD

#include "Windows.h"

#include "Msi.h"

#include <string>

#pragma comment(lib, "msi.lib")

namespace bluespy {
namespace detail {
inline std::wstring get_msi_property(const wchar_t* product_code, const wchar_t* property) {
    std::wstring data;
    DWORD data_size;
    for (;;) {
        data_size = data.size();
        auto r = MsiGetProductInfoW(product_code, property, &data[0], &data_size);
        if (r != ERROR_MORE_DATA)
            break;
        data.resize(data_size + 1);
    }
    data.resize(data_size);
    return data;
}

inline bool load_from_dir(std::wstring path) {
    if (path.size() >= 4 && path.find(L".dll", path.size() - 4) == path.npos) {
        if (!path.empty() && path.back() != L'\\' && path.back() != L'/')
            path.push_back(L'\\');
        path.append(L"libblueSPY.dll");
    }
    return LoadLibraryExW(path.c_str(), NULL,
                          LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
}

inline std::wstring get_env(const wchar_t* name) {
    std::wstring env;
    size_t envsize = 0;
    do {
        env.resize(envsize);
        _wgetenv_s(&envsize, env.empty() ? nullptr : &env[0], env.size(), name);
    } while (envsize != env.size());
    if (env.size() && env.back() == '\0')
        env.pop_back();
    return env;
}
} // namespace detail
} // namespace bluespy

/**
 * @brief Find and load the bluespy DLL - Windows only.
 * @param[in] override_path - Path to an extracted blueSPY. If nullptr, tries to find the
 * installed version.
 * @return Success
 *
 * Combine with linker flag /DELAYLOAD:libblueSPY.dll to link to the installed DLL while avoiding
 * the need to change any DLL search paths or copy DLLs.
 */
inline bool bluespy_dll_load(const wchar_t* override_path) {
    using namespace bluespy::detail;
    auto env_path = get_env(L"BLUESPY_LIBRARY_PATH");
    if (override_path) {
        if (load_from_dir(override_path)) {
            return true;
        }
    } else if (!env_path.empty()) {
        if (load_from_dir(env_path)) {
            return true;
        }
    } else {
        wchar_t product_code[39];
        for (DWORD product_index = 0;
             MsiEnumRelatedProductsW(L"{49458F73-64D6-4F28-8DA6-CCE6BC6C13AB}", 0, product_index,
                                     product_code) == ERROR_SUCCESS;
             ++product_index) {
            if (get_msi_property(product_code, L"ProductName") == L"blueSPY" &&
                load_from_dir(get_msi_property(product_code, L"InstallLocation"))) {
                return true;
            }
        }
    }
    return false;
}

#endif