using System;
using System.Timers;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core;
///
/// A class that represents a countdown timer with feedbacks for time remaining, percent, and seconds
///
public class SecondsCountdownTimer: IKeyed
{
///
/// Event triggered when the timer starts.
///
public event EventHandler HasStarted;
///
/// Event triggered when the timer finishes.
///
public event EventHandler HasFinished;
///
/// Event triggered when the timer is cancelled.
///
public event EventHandler WasCancelled;
///
public string Key { get; private set; }
///
/// Indicates whether the timer is currently running
///
public BoolFeedback IsRunningFeedback { get; private set; }
bool _isRunning;
///
/// Feedback for the percentage of time remaining
///
public IntFeedback PercentFeedback { get; private set; }
///
/// Feedback for the time remaining in a string format
//
public StringFeedback TimeRemainingFeedback { get; private set; }
///
/// Feedback for the time remaining in seconds
///
public IntFeedback SecondsRemainingFeedback { get; private set; }
///
/// When true, the timer will count down immediately upon calling Start. When false, the timer will count up, and when Finish is called, it will stop counting and fire the HasFinished event.
///
public bool CountsDown { get; set; }
///
/// The number of seconds to countdown
///
public int SecondsToCount { get; set; }
///
/// The time at which the timer was started. Used to calculate percent and time remaining. Will be DateTime.MinValue if the timer is not currently running.
///
public DateTime StartTime { get; private set; }
///
/// The time at which the timer will finish counting down. Used to calculate percent and time remaining. Will be DateTime.MinValue if the timer is not currently running.
///
public DateTime FinishTime { get; private set; }
private Timer _secondTimer;
///
/// Constructor
///
///
public SecondsCountdownTimer(string key)
{
Key = key;
IsRunningFeedback = new BoolFeedback(() => _isRunning);
TimeRemainingFeedback = new StringFeedback(() =>
{
// Need to handle up and down here.
var timeSpan = FinishTime - DateTime.Now;
Debug.LogMessage(LogEventLevel.Verbose,
"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}", this,
timeSpan.Minutes, timeSpan.Seconds, timeSpan.TotalSeconds);
if (Math.Floor(timeSpan.TotalSeconds) < 60 && Math.Floor(timeSpan.TotalSeconds) >= 0) //ignore milliseconds
{
return String.Format("{0:00}", timeSpan.Seconds);
}
return Math.Floor(timeSpan.TotalSeconds) < 0
? "00"
: String.Format("{0:00}:{1:00}", timeSpan.Minutes, timeSpan.Seconds);
});
SecondsRemainingFeedback = new IntFeedback(() => (int)(FinishTime - DateTime.Now).TotalSeconds);
PercentFeedback =
new IntFeedback(
() =>
(int)
(Math.Floor((FinishTime - DateTime.Now).TotalSeconds)/
Math.Floor((FinishTime - StartTime).TotalSeconds)*100));
}
///
/// Starts the Timer
///
public void Start()
{
if (_isRunning)
return;
StartTime = DateTime.Now;
FinishTime = StartTime + TimeSpan.FromSeconds(SecondsToCount);
if (_secondTimer != null)
_secondTimer.Stop();
_secondTimer = new Timer(1000) { AutoReset = true };
_secondTimer.Elapsed += (s, e) => SecondElapsedTimerCallback(null);
_secondTimer.Start();
SecondElapsedTimerCallback(null);
_isRunning = true;
IsRunningFeedback.FireUpdate();
var handler = HasStarted;
if (handler != null)
handler(this, new EventArgs());
}
///
/// Restarts the timer
///
public void Reset()
{
_isRunning = false;
IsRunningFeedback.FireUpdate();
Start();
}
///
/// Cancels the timer (without triggering it to finish)
///
public void Cancel()
{
StopHelper();
var handler = WasCancelled;
if (handler != null)
handler(this, new EventArgs());
}
///
/// Called upon expiration, or calling this will force timer to finish.
///
public void Finish()
{
StopHelper();
var handler = HasFinished;
if (handler != null)
handler(this, new EventArgs());
}
void StopHelper()
{
if (_secondTimer != null)
{
_secondTimer.Stop();
_secondTimer = null;
}
_isRunning = false;
IsRunningFeedback.FireUpdate();
}
void SecondElapsedTimerCallback(object o)
{
if (DateTime.Now >= FinishTime)
{
Finish();
return;
}
PercentFeedback.FireUpdate();
TimeRemainingFeedback.FireUpdate();
SecondsRemainingFeedback.FireUpdate();
}
}