using ytLive.Helpers;
using ytLive.Models;
namespace ytLive.ViewModels;
///
/// Preview undo/redo (creator directive 2026-11-01): Ctrl+Z and Ctrl+Shift+Z in edit mode.
///
/// The stack lives on the view model rather than in PreviewPane because the edit
/// surface is not one control — a layer can be moved by a drag, removed from the layers list, or
/// hidden from the properties panel — and undo has to cover all of them or it is a lie.
///
/// Gated on edit mode. Undo only means something while the preview is the edit
/// surface; with editing off the preview shows program output and a stray Ctrl+Z would silently
/// rewrite a scene the creator isn't looking at.
///
public partial class MainViewModel
{
private readonly PreviewHistory _previewHistory = new();
public bool CanUndoPreview => EditingActive && _previewHistory.CanUndo;
public bool CanRedoPreview => EditingActive && _previewHistory.CanRedo;
public RelayCommand UndoPreviewCommand { get; private set; } = null!;
public RelayCommand RedoPreviewCommand { get; private set; } = null!;
/// Call immediately BEFORE a layout mutation — once per gesture, never per mouse
/// move, or a single drag eats the whole stack.
internal void PushPreviewUndo()
{
if (!EditingActive) return;
_previewHistory.Capture(Scenes);
NotifyHistoryChanged();
}
private void UndoPreview()
{
if (!EditingActive || !_previewHistory.Undo(Scenes)) return;
AfterHistoryStep();
}
private void RedoPreview()
{
if (!EditingActive || !_previewHistory.Redo(Scenes)) return;
AfterHistoryStep();
}
private void AfterHistoryStep()
{
// Restored geometry has to reach the compositor: the baked region is cached per scene
// and keyed on element identity, so a pure value change is invisible to it until the
// cache is dropped. Without this, undo moves the model and the picture stays put.
foreach (var scene in Scenes) _sceneGraph.InvalidateBake(scene);
ScheduleSave();
NotifyHistoryChanged();
}
private void NotifyHistoryChanged()
{
OnPropertyChanged(nameof(CanUndoPreview));
OnPropertyChanged(nameof(CanRedoPreview));
}
private void InitPreviewHistory()
{
// RelayCommand queries CommandManager.RequerySuggested, so no explicit invalidation is
// needed when CanExecute changes.
UndoPreviewCommand = new RelayCommand(_ => UndoPreview(), _ => CanUndoPreview);
RedoPreviewCommand = new RelayCommand(_ => RedoPreview(), _ => CanRedoPreview);
}
/// Dropped on layout load: undoing across a reload would resurrect a layout the
/// creator never had.
private void ClearPreviewHistory()
{
_previewHistory.Clear();
NotifyHistoryChanged();
}
}