namespace RioJoy.Core.Profiles;
/// Supplies the current foreground application's executable name.
public interface IForegroundProcessProvider
{
/// The foreground executable name (e.g. "game.exe"), or null if unknown.
string? GetForegroundExecutable();
}
///
/// Polls the foreground process and resolves the three-state auto-switch decision,
/// raising only when the decision actually changes.
/// The polling cadence is driven by the host; performs one
/// resolution so the logic is deterministic and unit-testable.
///
public sealed class AutoSwitchWatcher
{
private readonly IForegroundProcessProvider _provider;
private readonly Func _configAccessor;
private SwitchDecision? _last;
public AutoSwitchWatcher(IForegroundProcessProvider provider, Func configAccessor)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
_configAccessor = configAccessor ?? throw new ArgumentNullException(nameof(configAccessor));
}
/// Raised when the resolved decision differs from the previous one.
public event Action? DecisionChanged;
/// The most recently resolved decision (null until the first ).
public SwitchDecision? Current => _last;
///
/// Forget the last decision so the next re-raises
/// even if the resolved decision is unchanged. Used
/// after a manual/editor override to re-sync the coordinator with the foreground app.
///
public void Reset() => _last = null;
/// Resolve once; raise if it changed. Returns the decision.
public SwitchDecision Poll()
{
string? exe = _provider.GetForegroundExecutable();
SwitchDecision decision = AutoSwitchResolver.Resolve(_configAccessor(), exe);
if (_last is null || !_last.Value.Equals(decision))
{
_last = decision;
DecisionChanged?.Invoke(decision);
}
return decision;
}
/// Poll on until cancelled.
public async Task RunAsync(TimeSpan interval, CancellationToken cancellationToken)
{
// net48 has no PeriodicTimer; poll on a Task.Delay loop instead.
Poll();
while (!cancellationToken.IsCancellationRequested)
{
try
{
await Compat.TaskCompat.Delay(interval, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}
Poll();
}
}
}