canto/Assets/Scripts/System/Duration.cs
2025-07-20 01:20:05 -04:00

182 lines
5.9 KiB
C#

using System;
using NUnit.Framework.Constraints;
using UnityEngine;
using UnityEngine.UIElements;
namespace KitsuneCafe.System
{
public enum TimeUnit : byte
{
Ticks,
Milliseconds,
Seconds,
Minutes,
Hours,
Days
}
[Serializable]
public struct Duration : IComparable, IComparable<Duration>, IComparable<TimeSpan>, IEquatable<Duration>, IEquatable<TimeSpan>, ISerializationCallbackReceiver
{
[SerializeField]
private double displayValue;
[SerializeField, HideInInspector]
private long value;
[SerializeField]
private TimeUnit unit;
public Duration(long ticks, TimeUnit unit)
{
displayValue = ToDisplayValue(ticks, 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)
{
return unit switch
{
TimeUnit.Ticks => ticks,
TimeUnit.Milliseconds => ticks / TimeSpan.TicksPerMillisecond,
TimeUnit.Seconds => ticks / TimeSpan.TicksPerSecond,
TimeUnit.Minutes => ticks / TimeSpan.TicksPerMinute,
TimeUnit.Hours => ticks / TimeSpan.TicksPerHour,
TimeUnit.Days => ticks / TimeSpan.TicksPerHour,
var x => throw new ArgumentException($"{x} is not a valid TimeUnit.")
};
}
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)}";
}
public void OnBeforeSerialize()
{
displayValue = Into();
}
public void OnAfterDeserialize()
{
value = FromDisplayValue(displayValue, unit);
}
}
}