using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.LogicalTree;
using IFSEngine.Animation;
using IFSRenderer.AvaloniaShell.Tests.Fakes;
using Xunit;
namespace IFSRenderer.AvaloniaShell.Tests;
/// <summary>
/// Editing a keyframe has to reach the viewport.
/// </summary>
/// <remarks>
/// <para>
/// "if you edit a keyframe there it won't update the viewport until you change the frame."
/// </para>
/// <para>
/// The same ORDERING bug this harness was built for, in a third place. <c>TimeChanged</c>
/// re-evaluated the dopesheet onto the fractal and invalidated the params buffer;
/// <c>Changed</c> - which every keyframe edit raises - pushed the panel state and stopped. The
/// fractal kept the values the last <c>EvaluateAt</c> had written, so the edit became visible
/// only when the playhead next moved and re-evaluated for its own reasons.
/// </para>
/// <para>
/// Asserted through the queue rather than by reading the fractal: the point is that the engine
/// was asked to re-render, and <see cref="FakeRenderQueue"/> defers exactly as the real service
/// does, so a fix that merely mutated the model on the UI thread would not pass.
/// </para>
/// </remarks>
public class AnimationViewportRefreshTests
{
[AvaloniaFact]
public void Editing_a_keyframe_re_evaluates_without_moving_the_playhead()
{
(MainWindow window, FakeRenderQueue queue) = MainWindowHarness.Start();
// By name, and through the LOGICAL tree: the animation panel lives in a tab that the
// headless run never realizes, so it has no visual tree to walk.
AnimationPanel panel = window.FindControl<AnimationPanel>("AnimationView");
Assert.NotNull(panel);
Dopesheet sheet = queue.Current.Dopesheet;
// Two keys on one channel, made the way the ◇ diamond makes them.
foreach ((double t, double v) in new[] { (0.0, 1.0), (4.0, 5.0) })
sheet.AddOrUpdateChannel("Brightness", "Brightness",
TimeOnly.FromTimeSpan(TimeSpan.FromSeconds(t)), v);
panel.SetFractal(queue.Current);
panel.ReloadChannels();
MainWindowHarness.Settle(queue);
CurveCanvas curve = panel.GetLogicalDescendants().OfType<CurveCanvas>().Single();
Keyframe key = sheet.Channels["Brightness"].Keyframes[0];
curve.BeginDrag(key, new Point(0, 0));
queue.Renderer.Calls.Clear();
int postsBefore = queue.PostCount;
// The edit, with the playhead left exactly where it was.
key.Value = 42;
curve.EndDrag();
MainWindowHarness.Settle(queue);
Assert.True(queue.PostCount > postsBefore,
"editing a keyframe queued no engine work at all");
Assert.Contains(nameof(FakeRenderer.InvalidateParamsBuffer), queue.Renderer.Calls);
// The evaluation ran, so the fractal now carries the edited value.
Assert.Equal(42, queue.Current.Brightness, 6);
}
}