using Cthangover.Core.UI.Event;
using Godot;
using Cthangover.Core.UI;
using Cthangover.Mods.Interface.Base.Lists;
using Cthangover.Core.UI.Dialog;
using Cthangover.Core.UI.Executable;
using Cthangover.Core.Scenarios;
using Cthangover.Core.Interactive;
namespace Cthangover.Mods.Dialog.Action.Impls
{
///
/// Timeline-based update action: registers itself as an IOnUpdateEvent
/// subscriber and fires OnUpdateCallback(float t) each frame with a normalized
/// progress value (0→1) over WaitTime seconds. WaitType is WaitEvent so the
/// dialog runtime pauses until elapsed >= WaitTime, at which point the action
/// completes itself through (event-driven —
/// no DialogBox poll) and the box advances the queue, scheduling the timer of a
/// following delay like any other advance. OnStartCallback/OnFinishCallback
/// bracket the animation for setup/teardown. Progress follows the controller's
/// process delta, so the timeline lasts WaitTime seconds of game time at any frame
/// rate (and scales with Engine.TimeScale exactly like delay timers).
///
public class ActionTimedUpdate : ActionCommand, IOnUpdateEvent
{
/// Update priority. Value 0 — runs before most other listeners.
public int Priority => 0;
/// Fires once when is called, before the first update frame.
public System.Action OnStartCallback { get; set; }
/// Fires each frame during the active period with a normalized progress value t (0→1) over seconds.
public System.Action OnUpdateCallback { get; set; }
/// Fires once when the elapsed time exceeds or when destructed.
public System.Action OnFinishCallback { get; set; }
/// Pauses the dialog until the elapsed time meets WaitTime — the action then completes itself.
public override WaitType WaitType { get; set; } = WaitType.WaitEvent;
/// Total duration in seconds over which receives normalized progress.
public override float WaitTime { get; set; } = 1f;
private float elapsed;
private bool isRunning;
private DialogRuntime runtime;
private SceneEventController controller;
/// Registers this action as an listener on the scene's event controller.
public override void DoConstruct()
{
base.DoConstruct();
controller = Scene.Find("EventController");
controller?.AddUpdateEventListener(this);
}
/// Starts the timeline: resets elapsed time and fires before the first frame.
public override void DoRun(DialogRuntime runtime)
{
this.runtime = runtime;
elapsed = 0f;
isRunning = true;
OnStartCallback?.Invoke();
}
/// Detaches from the update controller, stops ticking and fires .
public override void DoDestruct()
{
base.DoDestruct();
isRunning = false;
runtime = null;
controller?.RemoveUpdateEventListener(this);
controller = null;
OnFinishCallback?.Invoke();
}
void IOnUpdateEvent.OnUpdate()
{
if (!isRunning)
return;
elapsed += (float)(controller?.GetProcessDeltaTime() ?? 0d);
var t = WaitTime > 0f ? Mathf.Min(elapsed / WaitTime, 1f) : 1f;
OnUpdateCallback?.Invoke(t);
if (elapsed < WaitTime)
return;
// Advancing destructs this action, which clears the runtime field:
// keep the runtime in a local for the whole completion.
isRunning = false;
var owner = runtime;
if (owner?.DialogBox != null)
{
owner.DialogBox.CompleteAction(this);
}
else if (owner != null)
{
owner.Next();
owner.Run();
}
}
}
}