PlotDirector/PlotLine/Services/CharacterAgeDisplayService.cs

117 lines
3.6 KiB
C#

using PlotLine.Models;
namespace PlotLine.Services;
public sealed class CharacterAgeDisplayResult
{
public bool HasExactAge { get; init; }
public string? Label { get; init; }
public string? DisplayText { get; init; }
}
public static class CharacterAgeDisplayService
{
public static CharacterAgeDisplayResult GetAgeForScene(SceneCharacter character, string? sceneTimeModeName, DateTime? sceneStartDateTime)
{
var effectiveDate = ResolveEffectiveSceneDate(sceneTimeModeName, sceneStartDateTime);
if (character.BirthDate.HasValue && effectiveDate.HasValue)
{
return new CharacterAgeDisplayResult
{
HasExactAge = true,
Label = "Age in scene",
DisplayText = FormatExactAge(character.BirthDate.Value.Date, effectiveDate.Value.Date)
};
}
if (character.AgeAtSeriesStart.HasValue)
{
return new CharacterAgeDisplayResult
{
Label = "Approximate age",
DisplayText = $"About {character.AgeAtSeriesStart.Value}"
};
}
return new CharacterAgeDisplayResult();
}
public static CharacterAgeDisplayResult GetAgeForScene(Character character, string? sceneTimeModeName, DateTime? sceneStartDateTime)
{
var effectiveDate = ResolveEffectiveSceneDate(sceneTimeModeName, sceneStartDateTime);
if (character.BirthDate.HasValue && effectiveDate.HasValue)
{
return new CharacterAgeDisplayResult
{
HasExactAge = true,
Label = "Age in scene",
DisplayText = FormatExactAge(character.BirthDate.Value.Date, effectiveDate.Value.Date)
};
}
if (character.AgeAtSeriesStart.HasValue)
{
return new CharacterAgeDisplayResult
{
Label = "Approximate age",
DisplayText = $"About {character.AgeAtSeriesStart.Value}"
};
}
return new CharacterAgeDisplayResult();
}
public static DateTime? ResolveEffectiveSceneDate(string? timeModeName, DateTime? startDateTime)
{
if (!startDateTime.HasValue)
{
return null;
}
return string.Equals(timeModeName, "Exact Date", StringComparison.OrdinalIgnoreCase)
|| string.Equals(timeModeName, "Exact DateTime", StringComparison.OrdinalIgnoreCase)
|| string.Equals(timeModeName, "Exact Date and Time", StringComparison.OrdinalIgnoreCase)
? startDateTime.Value.Date
: null;
}
public static string FormatExactAge(DateTime birthDate, DateTime sceneDate)
{
var (years, months) = CalculateExactAge(birthDate, sceneDate);
return months == 0
? $"{years} year{Pluralise(years)}"
: $"{years} year{Pluralise(years)}, {months} month{Pluralise(months)}";
}
public static (int Years, int Months) CalculateExactAge(DateTime birthDate, DateTime sceneDate)
{
var years = sceneDate.Year - birthDate.Year;
if (sceneDate < birthDate.AddYears(years))
{
years--;
}
if (years < 0)
{
years = 0;
}
var lastBirthday = birthDate.AddYears(years);
var months = ((sceneDate.Year - lastBirthday.Year) * 12) + sceneDate.Month - lastBirthday.Month;
if (sceneDate.Day < lastBirthday.Day)
{
months--;
}
if (months < 0)
{
months = 0;
}
return (years, months);
}
private static string Pluralise(int value) => value == 1 ? string.Empty : "s";
}