using System;
using System.Runtime.InteropServices;
using Windows.Graphics.DirectX.Direct3D11;
using WinRT;

namespace ytLive.Services;

/// <summary>
/// Bridges a native D3D11 device into the WinRT <see cref="IDirect3DDevice"/>
/// the capture API needs. CsWinRT doesn't project the Direct3D11Helper static,
/// so this creates the device with D3D11CreateDevice and converts it via the
/// WinRT interop export <c>CreateDirect3D11DeviceFromDXGIDevice</c> (d3d11.dll).
///
/// This deliberately does NOT use the older QI-for-IDirect3DDxgiInterfaceAccess
/// trick: the raw D3D11 device stopped exposing that interface on newer Windows
/// (verified E_NOINTERFACE on build 26200, hardware and WARP alike), while
/// CreateDirect3D11DeviceFromDXGIDevice keeps working. One shared device per
/// process.
/// </summary>
internal static class Direct3D11Helper
{
    private const uint D3D11CreateDeviceBgraSupport = 0x20;
    private const uint D3D11SdkVersion = 7;
    private const int DriverTypeHardware = 1;

    // IDirect3DDxgiInterfaceAccess (the legacy bridge) is only exposed on an
    // 11.1 device AND requires the 11.1 runtime to be in the requested set —
    // with pFeatureLevels = NULL D3D11CreateDevice never creates 11.1, so the
    // array must be explicit (11.1 first, then descending).
    private static readonly int[] FeatureLevels = { 0xB100, 0xB000, 0xA100, 0xA000, 0x9300, 0x9200, 0x9100 };

    private static IDirect3DDevice? _sharedDevice;
    private static readonly object Gate = new();

    public static IDirect3DDevice CreateDevice()
    {
        if (_sharedDevice != null) return _sharedDevice;
        lock (Gate)
        {
            return _sharedDevice ??= CreateDeviceCore();
        }
    }

    private static IDirect3DDevice CreateDeviceCore()
    {
        var hr = D3D11CreateDevice(IntPtr.Zero, DriverTypeHardware, IntPtr.Zero, D3D11CreateDeviceBgraSupport,
            FeatureLevels, (uint)FeatureLevels.Length, D3D11SdkVersion, out var devicePtr, out _, out var contextPtr);
        if (hr != 0)
            throw Marshal.GetExceptionForHR(hr)!;

        try
        {
            var dxgiGuid = new Guid("54EC77FA-1377-44E6-8C32-88FD5F44C84C");
            var qiHr = Marshal.QueryInterface(devicePtr, ref dxgiGuid, out var dxgiDevice);
            if (qiHr != 0)
                throw Marshal.GetExceptionForHR(qiHr)!;
            try
            {
                hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice, out var winrtDevice);
                if (hr != 0)
                    throw Marshal.GetExceptionForHR(hr)!;
                return MarshalInterface<IDirect3DDevice>.FromAbi(winrtDevice);
            }
            finally
            {
                Marshal.Release(dxgiDevice);
            }
        }
        finally
        {
            Marshal.Release(devicePtr);
            Marshal.Release(contextPtr);
        }
    }

    [DllImport("d3d11.dll")]
    private static extern int D3D11CreateDevice(IntPtr pAdapter, int driverType, IntPtr software,
        uint flags, int[]? featureLevels, uint featureLevelsCount, uint sdkVersion,
        out IntPtr device, out int featureLevel, out IntPtr immediateContext);

    [DllImport("d3d11.dll", ExactSpelling = true)]
    private static extern int CreateDirect3D11DeviceFromDXGIDevice(IntPtr dxgiDevice, out IntPtr graphicsDevice);
}
