using System.Collections.Generic;
using Jellyfin.Plugin.JRay.Models;
namespace Jellyfin.Plugin.JRay.Services;
///
/// Answers "which actors are in the scene at time t" from a truth file.
///
///
/// This is the unit that decides presence, so the scene-scoped semantics live
/// here rather than being spread through the controller.
///
///
/// A window is a claim about scene membership, not a recognition event.
/// An actor who turns away, is occluded, or is off-camera while the shot cuts to
/// whoever they are speaking to is still present. Two windows mean a genuine
/// departure and return, not a break in detection — so this code reads windows
/// exactly as given and never merges, splits, trims, or reorders them.
///
///
/// Bounds are inclusive at both ends, matching the format's definition. That
/// makes adjacent windows such as [0,10] and [10,20] both contain
/// t = 10; reporting the actor present once is correct, and is not a
/// reason to merge the windows.
///
///
// TRACES: JR-004, JR-005, JR-006 | SR-002
public static class PresenceLookup
{
///
/// Determines whether an actor is present in the scene at .
///
/// The actor entry from a truth file.
/// The timestamp, in seconds.
/// true when any window contains .
public static bool IsPresentAt(TruthActor actor, double t)
{
if (actor is null)
{
return false;
}
// A full scan, deliberately: windows may be numerous, but correctness
// must not depend on the producer having honoured the sortedness
// guarantee. An early exit on `start > t` would be faster and would
// silently under-report the moment one file arrived out of order —
// trading a correctness risk for a saving that does not matter at this
// scale (see JR-006).
foreach (var window in actor.Scenes)
{
if (window.Length == 2 && window[0] <= t && t <= window[1])
{
return true;
}
}
return false;
}
///
/// Lists the actors present in the scene at , in the
/// order the truth file lists them.
///
/// The truth file.
/// The timestamp, in seconds.
/// The actors whose windows contain .
public static IEnumerable ActorsPresentAt(TruthFile truth, double t)
{
if (truth is null)
{
yield break;
}
foreach (var actor in truth.Actors)
{
if (IsPresentAt(actor, t))
{
yield return actor;
}
}
}
///
/// Determines whether an actor's windows are sorted by start time, as the
/// truth-file format requires of producers.
///
///
/// Presence lookup does not depend on this — it is a diagnostic. A file that
/// fails it is still read correctly, but it signals a producer bug worth
/// surfacing rather than absorbing silently.
///
/// The actor entry from a truth file.
/// true when every window starts at or after its predecessor.
public static bool WindowsAreSorted(TruthActor actor)
{
if (actor is null)
{
return true;
}
double previousStart = double.NegativeInfinity;
foreach (var window in actor.Scenes)
{
if (window.Length != 2)
{
continue;
}
if (window[0] < previousStart)
{
return false;
}
previousStart = window[0];
}
return true;
}
}