using System.Globalization;
namespace decePubClient.Components.Nerd;
/// One line of a NerdTip; a row without a value is not shown.
public sealed record NerdRow(string Label, string Value)
{
public static NerdRow Of(string label, object value) => new(label, value switch
{
null => null,
DateTime time => Time(time),
double number => number.ToString("0.###", CultureInfo.InvariantCulture),
bool flag => flag ? "yes" : "no",
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),
_ => value.ToString()
});
/// A UTC time to the millisecond, as PrivaPub records it.
public static string Time(DateTime time) =>
DateTime.SpecifyKind(time, time.Kind == DateTimeKind.Unspecified ? DateTimeKind.Utc : time.Kind).ToUniversalTime()
.ToString("yyyy-MM-dd HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
/// A duration the way a person reads it: 850 ms, 12.4 s, 3 min 5 s, 2 h 10 min, 4 d 3 h.
public static string Duration(TimeSpan span)
{
var sign = span < TimeSpan.Zero ? "-" : string.Empty;
span = span.Duration();
return sign + (span.TotalSeconds switch
{
< 1 => $"{span.TotalMilliseconds:0} ms",
< 60 => $"{span.TotalSeconds.ToString("0.0", CultureInfo.InvariantCulture)} s",
< 3600 => $"{span.Minutes} min {span.Seconds} s",
< 86400 => $"{(int)span.TotalHours} h {span.Minutes} min",
_ => $"{(int)span.TotalDays} d {span.Hours} h"
});
}
/// A byte count: 912 B, 14.2 KiB, 3.1 MiB.
public static string Bytes(long? bytes) => bytes switch
{
null => null,
< 1024 => $"{bytes} B",
< 1024 * 1024 => $"{(bytes.Value / 1024d).ToString("0.0", CultureInfo.InvariantCulture)} KiB",
_ => $"{(bytes.Value / 1048576d).ToString("0.0", CultureInfo.InvariantCulture)} MiB"
};
/// The great-circle distance between two points in kilometres (haversine, Earth radius 6371 km).
public static double Kilometres(double latitude1, double longitude1, double latitude2, double longitude2)
{
const double radius = 6371;
double Radians(double degrees) => degrees * Math.PI / 180;
var dLatitude = Radians(latitude2 - latitude1);
var dLongitude = Radians(longitude2 - longitude1);
var a = Math.Pow(Math.Sin(dLatitude / 2), 2) + Math.Cos(Radians(latitude1)) * Math.Cos(Radians(latitude2)) * Math.Pow(Math.Sin(dLongitude / 2), 2);
return 2 * radius * Math.Asin(Math.Min(1, Math.Sqrt(a)));
}
}