using System.Reflection;
using System.Runtime.InteropServices;

namespace DragonISO.Engine.NdiInterop;

/// <summary>
/// Resolves the NDI native library (<c>Processing.NDI.Lib.x64.dll</c>) from the
/// NDI Runtime install directory exposed via <c>NDI_RUNTIME_DIR_V&lt;n&gt;</c>
/// environment variables.
///
/// The NDI installer creates these env vars but does not always add the runtime
/// directory to <c>PATH</c>, so a default <c>[DllImport("Processing.NDI.Lib.x64")]</c>
/// can fail with <c>0x8007007E</c> ("the specified module could not be found") on
/// otherwise correctly installed machines. This resolver patches that gap by
/// pre-loading the DLL from the install dir before the runtime falls back to its
/// default search algorithm.
///
/// The resolver is registered from the static constructor of <see cref="NdiNative"/>,
/// which guarantees it runs before the first P/Invoke on that type fires. On
/// non-Windows the resolver short-circuits; the assembly's P/Invokes are gated by
/// <c>[SupportedOSPlatform("windows")]</c> and won't fire there in any case.
/// </summary>
internal static class NdiNativeLibraryResolver
{
    private const string NdiX64LibraryName = "Processing.NDI.Lib.x64";
    private const string NdiX64LibraryFile = "Processing.NDI.Lib.x64.dll";

    /// <summary>
    /// NDI runtime install-dir environment variables, in preference order.
    /// V6 is what Dragon-ISO is built against; V5/V4 are listed as graceful fallbacks
    /// for installs that pre-date V6 â€” the runtime probe will still report a
    /// version mismatch, but at least the DLL will load and the engine can surface
    /// a clear alert instead of dying with DllNotFoundException.
    /// </summary>
    private static readonly string[] EnvVarFallbacks =
    {
        "NDI_RUNTIME_DIR_V6",
        "NDI_RUNTIME_DIR_V5",
        "NDI_RUNTIME_DIR_V4",
    };

    internal static void Register()
    {
        NativeLibrary.SetDllImportResolver(typeof(NdiNativeLibraryResolver).Assembly, Resolve);
    }

    private static IntPtr Resolve(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        if (libraryName != NdiX64LibraryName) return IntPtr.Zero;
        if (!OperatingSystem.IsWindows()) return IntPtr.Zero;

        foreach (var envVar in EnvVarFallbacks)
        {
            var dir = Environment.GetEnvironmentVariable(envVar);
            if (string.IsNullOrEmpty(dir)) continue;
            var path = Path.Combine(dir, NdiX64LibraryFile);
            if (!File.Exists(path)) continue;
            if (NativeLibrary.TryLoad(path, out var handle))
                return handle;
        }

        // Fall through to default loader (PATH, app dir, etc.) â€” preserves the
        // chance that someone added the NDI dir to PATH manually.
        return IntPtr.Zero;
    }
}
