using CPF.Mac.Foundation;
using CPF.Mac.ObjCRuntime;
using System;
using System.Runtime.InteropServices;

namespace CPF.Mac.CoreMedia
{
	public struct CMTime
	{
		[Flags]
		public enum Flags
		{
			Valid = 0x1,
			HasBeenRounded = 0x2,
			PositiveInfinity = 0x4,
			NegativeInfinity = 0x8,
			Indefinite = 0x10,
			ImpliedValueFlagsMask = 0x1C
		}

		public static CMTime Invalid;

		private const Flags kIndefinite = Flags.Valid | Flags.Indefinite;

		public static CMTime Indefinite;

		private const Flags kPositive = Flags.Valid | Flags.PositiveInfinity;

		public static CMTime PositiveInfinity;

		private const Flags kNegative = Flags.Valid | Flags.NegativeInfinity;

		public static CMTime NegativeInfinity;

		public static CMTime Zero;

		public const int MaxTimeScale = int.MaxValue;

		public long Value;

		public int TimeScale;

		public Flags TimeFlags;

		public long TimeEpoch;

		public static readonly NSString ValueKey;

		public static readonly NSString ScaleKey;

		public static readonly NSString EpochKey;

		public static readonly NSString FlagsKey;

		public bool IsInvalid => (TimeFlags & Flags.Valid) == 0;

		public bool IsIndefinite => (TimeFlags & (Flags.Valid | Flags.Indefinite)) == (Flags.Valid | Flags.Indefinite);

		public bool IsPositiveInfinity => (TimeFlags & (Flags.Valid | Flags.PositiveInfinity)) == (Flags.Valid | Flags.PositiveInfinity);

		public bool IsNegativeInfinity => (TimeFlags & (Flags.Valid | Flags.NegativeInfinity)) == (Flags.Valid | Flags.NegativeInfinity);

		public CMTime AbsoluteValue => CMTimeAbsoluteValue(this);

		public double Seconds => CMTimeGetSeconds(this);

		public IntPtr AsDictionary => CMTimeCopyAsDictionary(this, IntPtr.Zero);

		public string Description => NSString.FromHandle(CMTimeCopyDescription(IntPtr.Zero, this)).ToString();

		private CMTime(Flags f)
		{
			Value = 0L;
			TimeScale = 0;
			TimeEpoch = 0L;
			TimeFlags = f;
		}

		private CMTime(Flags f, int timescale)
		{
			Value = 0L;
			TimeScale = timescale;
			TimeEpoch = 0L;
			TimeFlags = f;
		}

		public CMTime(long value, int timescale)
		{
			Value = value;
			TimeScale = timescale;
			TimeFlags = Flags.Valid;
			TimeEpoch = 0L;
		}

		public CMTime(long value, int timescale, long epoch)
		{
			Value = value;
			TimeScale = timescale;
			TimeFlags = Flags.Valid;
			TimeEpoch = epoch;
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeAbsoluteValue(CMTime time);

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern int CMTimeCompare(CMTime time1, CMTime time2);

		public static int Compare(CMTime time1, CMTime time2)
		{
			return CMTimeCompare(time1, time2);
		}

		public static bool operator ==(CMTime time1, CMTime time2)
		{
			return Compare(time1, time2) == 0;
		}

		public static bool operator !=(CMTime time1, CMTime time2)
		{
			return !(time1 == time2);
		}

		public override bool Equals(object obj)
		{
			if (!(obj is CMTime))
			{
				return false;
			}
			return (CMTime)obj == this;
		}

		public override int GetHashCode()
		{
			return Value.GetHashCode() ^ TimeScale.GetHashCode() ^ TimeFlags.GetHashCode() ^ TimeEpoch.GetHashCode();
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeAdd(CMTime addend1, CMTime addend2);

		public static CMTime Add(CMTime time1, CMTime time2)
		{
			return CMTimeAdd(time1, time2);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeSubtract(CMTime minuend, CMTime subtraend);

		public static CMTime Subtract(CMTime minuend, CMTime subtraend)
		{
			return CMTimeSubtract(minuend, subtraend);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMultiply(CMTime time, int multiplier);

		public static CMTime Multiply(CMTime time, int multiplier)
		{
			return CMTimeMultiply(time, multiplier);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMultiplyByFloat64(CMTime time, double multiplier);

		public static CMTime Multiply(CMTime time, double multiplier)
		{
			return CMTimeMultiplyByFloat64(time, multiplier);
		}

		public static CMTime operator +(CMTime time1, CMTime time2)
		{
			return Add(time1, time2);
		}

		public static CMTime operator -(CMTime minuend, CMTime subtraend)
		{
			return Subtract(minuend, subtraend);
		}

		public static CMTime operator *(CMTime time, int multiplier)
		{
			return Multiply(time, multiplier);
		}

		public static CMTime operator *(CMTime time, double multiplier)
		{
			return Multiply(time, multiplier);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeConvertScale(CMTime time, int newScale, CMTimeRoundingMethod method);

		public CMTime ConvertScale(int newScale, CMTimeRoundingMethod method)
		{
			return CMTimeConvertScale(this, newScale, method);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern double CMTimeGetSeconds(CMTime time);

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMakeWithSeconds(double seconds, int preferredTimeScale);

		public static CMTime FromSeconds(double seconds, int preferredTimeScale)
		{
			return CMTimeMakeWithSeconds(seconds, preferredTimeScale);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMaximum(CMTime time1, CMTime time2);

		public static CMTime GetMaximum(CMTime time1, CMTime time2)
		{
			return CMTimeMaximum(time1, time2);
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMinimum(CMTime time1, CMTime time2);

		public static CMTime GetMinimum(CMTime time1, CMTime time2)
		{
			return CMTimeMinimum(time1, time2);
		}

		static CMTime()
		{
			Invalid = new CMTime((Flags)0);
			Indefinite = new CMTime(Flags.Valid | Flags.Indefinite);
			PositiveInfinity = new CMTime(Flags.Valid | Flags.PositiveInfinity);
			NegativeInfinity = new CMTime(Flags.Valid | Flags.NegativeInfinity);
			Zero = new CMTime(Flags.Valid, 1);
			IntPtr intPtr = Dlfcn.dlopen("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia", 0);
			if (intPtr != IntPtr.Zero)
			{
				try
				{
					ValueKey = Dlfcn.GetStringConstant(intPtr, "kCMTimeValueKey");
					ScaleKey = Dlfcn.GetStringConstant(intPtr, "kCMTimeScaleKey");
					EpochKey = Dlfcn.GetStringConstant(intPtr, "kCMTimeEpochKey");
					FlagsKey = Dlfcn.GetStringConstant(intPtr, "kCMTimeFlagsKey");
				}
				finally
				{
					Dlfcn.dlclose(intPtr);
				}
			}
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern IntPtr CMTimeCopyAsDictionary(CMTime time, IntPtr allocator);

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern IntPtr CMTimeCopyDescription(IntPtr allocator, CMTime time);

		public override string ToString()
		{
			return Description;
		}

		[DllImport("/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia")]
		private static extern CMTime CMTimeMakeFromDictionary(IntPtr dict);

		public static CMTime FromDictionary(IntPtr dict)
		{
			return CMTimeMakeFromDictionary(dict);
		}
	}
}
