using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using ytLive.Helpers;
using ytLive.Models;
using ytLive.Services;
using ytLive.Services.Compositor;
namespace ytLive.ViewModels;
/// The scoreboard strip: session tallies rendered as a full-width bar, per scene.
///
/// It is a layer, so a creator gets the same eye toggle,
/// drag and per-scene presence as every other element — "prominent on BRB, dialled back on Live" is
/// geometry, not a second mechanism.
public partial class MainViewModel
{
private readonly ScoreboardRecorder _scoreboard = new();
private ScoreboardTiles _scoreboardTiles = ScoreboardTiles.Default;
private ScoreboardStyle _scoreboardStyle = new();
private ScoreboardPresenter? _scoreboardPresenter;
private WriteableBitmap? _scoreboardPreview;
private Source? _scoreboardPreviewSource;
internal ScoreboardRecorder ScoreboardRecorder => _scoreboard;
internal ScoreboardTiles ScoreboardTilesState => _scoreboardTiles;
/// Composited strip frame for the output path (cached by the presenter).
internal VideoFrame? ScoreboardFrame => _scoreboardPresenter?.Frame;
internal void StartScoreboardSession()
{
_scoreboard.StartSession();
RefreshScoreboardPreviews();
}
internal void EndScoreboardSession()
{
_scoreboard.EndSession();
RefreshScoreboardPreviews();
}
private void InitScoreboard()
{
_scoreboardPresenter = new ScoreboardPresenter(_scoreboard, _scoreboardStyle)
{
// placeholder so SetTiles below drives the first render
};
_scoreboardPresenter.SetTiles(_scoreboardTiles);
_scoreboardPresenter.SetStyle(_scoreboardStyle);
_scoreboard.Changed += RefreshScoreboardPreviews;
}
/// Snapshot of the strip presenters as themed properties, for the properties panel
/// (unit 7) — kept here so the panel binds one object instead of six primitives.
internal ScoreboardStyle ScoreboardStyleState => _scoreboardStyle;
/// Publishes the strip to every scoreboard layer's preview element. The pump thread
/// must never touch a WriteableBitmap, so this runs on the UI thread only.
private void RefreshScoreboardPreviews()
{
var frame = _scoreboardPresenter?.Frame;
foreach (var scene in Scenes)
foreach (var source in scene.Elements.OfType().Where(s => s.IsScoreboard))
{
if (frame is null) { source.VideoImageSource = null; continue; }
if (_scoreboardPreview is null || _scoreboardPreview.PixelWidth != frame.Width ||
_scoreboardPreview.PixelHeight != frame.Height)
{
_scoreboardPreview = new WriteableBitmap(frame.Width, frame.Height, 96, 96, PixelFormats.Bgra32, null);
}
_scoreboardPreview.WritePixels(new System.Windows.Int32Rect(0, 0, frame.Width, frame.Height),
frame.BgraPixels, frame.Width * 4, 0);
source.VideoImageSource = _scoreboardPreview;
_scoreboardPreviewSource = source;
}
}
}