using Editor.PluginInterface;
using HmEG;
using Recordingtest.Bridge;
using Recordingtest.Hmeg.Bridge;
namespace Recordingtest.Sut.EgBim.PluginHost;
///
/// MEF/PluginLoader-discovered plugin. Inherits the SUT's EditorPlugin
/// abstract base (which itself implements HmEG.IPlugin), and on construction
/// boots a localhost HTTP bridge that exposes HmEG state to recordingtest.
///
public sealed class HmEgBridgePlugin : EditorPlugin, IDisposable
{
private BridgeHttpServer? _server;
public HmEgBridgePlugin()
{
StartBridge();
}
public override string Name => "Recordingtest.Sut.EgBim.PluginHost";
public override string Description => "recordingtest engine-bridge v3 (HTTP sidecar)";
protected override void Initialize()
{
// Construction already started the bridge; Initialize is a no-op safeguard.
}
private void StartBridge()
{
try
{
var port = PortResolver.Resolve();
var provider = BuildProvider();
var router = new StateRouter(provider, port);
_server = new BridgeHttpServer(router, port);
_server.Start();
}
catch
{
// never throw out of plugin construction; SUT must remain stable.
}
}
///
/// Choose the best available :
///
/// 1. HmegDirectStateProvider — HmEG-aware tier, calls into HmEG
/// public API. Reusable across any HmEG-hosting WPF app. Active
/// space/viewport handles are resolved via lambdas; the EgBim
/// app-specific entry-point lookup lives in this method only.
/// 2. ReflectionEngineStateProvider — fallback that uses
/// IAppManagerAccessor reflection on Editor.AppManager.AppManager.
/// Used when the direct provider can't resolve any handle.
///
/// Both providers are wrapped to never throw; the SUT must remain stable.
///
private IEngineStateProvider BuildProvider()
{
// EG-BIM Modeler entry points (resolved via EditorPlugin base):
// RootSpace = AppManager.ViewportManager.RootSpace
// View (EGViewport : HmEGViewport) — plugin-injected active viewport
// AppManager.FileManager.CurrentFile — document path on disk
//
// Each lambda is wrapped in try/catch so plugin construction never
// throws even if the host state is in flight at boot time.
Func spaceProvider = () =>
{
try { return RootSpace; }
catch { return null; }
};
Func viewportProvider = () =>
{
// EditorPlugin.View is only populated when the plugin is actually
// Run() by a user command; our bridge plugin lives as a long-
// running HTTP server and never runs a trigger, so View stays
// null. Instead pull the active viewport from the global
// ViewportManager, preferring FocusedViewport, then falling back
// to any registered viewport. EGViewport implements HmEGViewport.
try
{
var vm = AppManager?.ViewportManager;
if (vm is null) return null;
var focused = vm.FocusedViewport;
if (focused is not null) return focused;
var any = vm.Viewports;
if (any is null) return null;
foreach (var v in any)
{
if (v is not null) return v;
}
return null;
}
catch { return null; }
};
Func documentPathProvider = () =>
{
try
{
var path = AppManager?.FileManager?.CurrentFile;
return string.IsNullOrEmpty(path) ? null : path;
}
catch { return null; }
};
var direct = new HmegDirectStateProvider(spaceProvider, viewportProvider, documentPathProvider);
var fallback = new ReflectionEngineStateProvider(this);
return new ChainedEngineStateProvider(direct, fallback);
}
public void Dispose()
{
try { _server?.Dispose(); } catch { }
_server = null;
}
}