using System; using UnityEngine; using UnityEngine.UIElements; namespace KitsuneCafe.Sys { public enum TimeUnit : byte { Ticks, Milliseconds, Seconds, Minutes, Hours, Days } [Serializable] public struct Duration : IComparable, IComparable, IComparable, IEquatable, IEquatable { [SerializeField, HideInInspector] private long value; [SerializeField] private TimeUnit unit; public Duration(long ticks, TimeUnit unit) { value = ticks; this.unit = unit; } public Duration(long ticks) : this(ticks, TimeUnit.Ticks) { } public static implicit operator Duration(TimeSpan ts) => new(ts.Ticks, TimeUnit.Ticks); public static implicit operator TimeSpan(Duration duration) => duration.AsTimeSpan(); public static implicit operator TimeValue(Duration duration) => duration.AsTimeValue(); public static Duration operator +(Duration lhs, Duration rhs) => lhs.AsTimeSpan() + rhs.AsTimeSpan(); public readonly TimeSpan AsTimeSpan() => new(value); public readonly TimeValue AsTimeValue() => new((float)Into(TimeUnit.Seconds)); public static Duration From(long value, TimeUnit unit) { var ticks = unit switch { TimeUnit.Ticks => value, TimeUnit.Milliseconds => TimeSpan.TicksPerMillisecond * value, TimeUnit.Seconds => TimeSpan.TicksPerSecond * value, TimeUnit.Minutes => TimeSpan.TicksPerMinute * value, TimeUnit.Hours => TimeSpan.TicksPerHour * value, TimeUnit.Days => TimeSpan.TicksPerHour * value, var x => throw new ArgumentException($"{x} is not a valid TimeUnit.") }; return new Duration(ticks, unit); } public static Duration From(double value, TimeUnit unit) { var ticks = unit switch { TimeUnit.Ticks => value, TimeUnit.Milliseconds => TimeSpan.TicksPerMillisecond * value, TimeUnit.Seconds => TimeSpan.TicksPerSecond * value, TimeUnit.Minutes => TimeSpan.TicksPerMinute * value, TimeUnit.Hours => TimeSpan.TicksPerHour * value, TimeUnit.Days => TimeSpan.TicksPerHour * value, var x => throw new ArgumentException($"{x} is not a valid TimeUnit.") }; return new Duration(Convert.ToInt64(ticks), unit); } public static double ToDisplayValue(long ticks, TimeUnit unit) { decimal value = unit switch { TimeUnit.Ticks => ticks, TimeUnit.Milliseconds => (decimal)ticks / TimeSpan.TicksPerMillisecond, TimeUnit.Seconds => (decimal)ticks / TimeSpan.TicksPerSecond, TimeUnit.Minutes => (decimal)ticks / TimeSpan.TicksPerMinute, TimeUnit.Hours => (decimal)ticks / TimeSpan.TicksPerHour, TimeUnit.Days => (decimal)ticks / TimeSpan.TicksPerHour, var x => throw new ArgumentException($"{x} is not a valid TimeUnit.") }; return Convert.ToDouble(value); } public static long FromDisplayValue(double value, TimeUnit unit) { var ticks = unit switch { TimeUnit.Ticks => value, TimeUnit.Milliseconds => TimeSpan.TicksPerMillisecond * value, TimeUnit.Seconds => TimeSpan.TicksPerSecond * value, TimeUnit.Minutes => TimeSpan.TicksPerMinute * value, TimeUnit.Hours => TimeSpan.TicksPerHour * value, TimeUnit.Days => TimeSpan.TicksPerHour * value, var x => throw new ArgumentException($"{x} is not a valid TimeUnit.") }; return Convert.ToInt64(ticks); } public readonly double Into() => Into(unit); public readonly double Into(TimeUnit unit) { return ToDisplayValue(value, unit); } public readonly int CompareTo(object obj) { return obj switch { Duration d => CompareTo(d), TimeSpan ts => CompareTo(ts), TimeValue tv => CompareTo(tv), var value => throw new ArgumentException($"{value} is not comparable to Duration") }; } public static bool Equals(Duration a, Duration b) { return a.value == b.value; } public static int Compare(Duration a, Duration b) { return a.value.CompareTo(b.value); } public readonly int CompareTo(Duration other) { return Compare(this, other); } public readonly int CompareTo(TimeSpan other) { return TimeSpan.Compare(this, other); } public readonly bool Equals(Duration other) { return Equals(this, other); } public readonly bool Equals(TimeSpan other) { return other.Equals(this); } public override readonly bool Equals(object obj) { return (obj is Duration d && Equals(this, d)) || (obj is TimeSpan ts && Equals(this, ts)); } public override readonly int GetHashCode() { return HashCode.Combine(value); } public override readonly string ToString() { return $"{value} {nameof(unit)}"; } } }