namespace AcDream.Core.Journal;
///
/// One page of retail's per-character journal.
///
///
/// Entirely client-authored: no wire, no server, no dat. The player writes it.
/// Retail's own PageInfo (64 bytes, g_JournalPages).
///
/// Short name, shown in the Page List. Authored max 16 characters.
/// Page title. Authored max 32.
/// Free-form body. Authored max 2048.
/// Countdown days.
/// Countdown hours.
/// Countdown minutes.
///
/// Recorded location, in retail's own north-south / east-west units — what the
/// "Record" button stamps and the page shows as "00.0S, 00.0W".
///
/// See .
///
/// Whether a location was ever recorded. Distinct from (0, 0), which is a real
/// place.
///
///
/// The running countdown's remaining seconds, or 0 when the timer is not
/// running. Retail's <TIME>.
///
public sealed record JournalPage(
string Label = "",
string Title = "",
string Notes = "",
int TimerDays = 0,
int TimerHours = 0,
int TimerMinutes = 0,
float LocationX = 0f,
float LocationY = 0f,
bool HasLocation = false,
double RunningTimerSeconds = 0d)
{
/// Authored 0x1E on the label edit box.
public const int MaxLabelLength = 16;
/// Authored 0x1E on the title edit box.
public const int MaxTitleLength = 32;
/// Authored 0x1E on the notes edit box.
public const int MaxNotesLength = 2048;
/// An untouched page — what "New" produces.
public static readonly JournalPage Empty = new();
/// Whether the timer fields describe any duration at all.
public bool HasTimer =>
TimerDays != 0 || TimerHours != 0 || TimerMinutes != 0;
/// The timer fields as a single duration.
public TimeSpan TimerDuration =>
new(TimerDays, TimerHours, TimerMinutes, 0);
/// Whether a countdown is currently running on this page.
public bool IsTimerRunning => RunningTimerSeconds > 0d;
///
/// The page with every field clipped to its authored maximum. Applied at
/// the seams that accept outside text — a load from disk, or a paste.
///
public JournalPage Clipped() => this with
{
Label = Clip(Label, MaxLabelLength),
Title = Clip(Title, MaxTitleLength),
Notes = Clip(Notes, MaxNotesLength),
};
private static string Clip(string value, int max) =>
value.Length <= max ? value : value[..max];
}