using System;
using System.Collections.Generic;
using System.Reactive.Disposables;

namespace System.Reactive.Concurrency
{
	public abstract class ScheduledItem<T> : IScheduledItem<T>, IComparable<ScheduledItem<T>>, IDisposable where T : IComparable<T>
	{
		protected ScheduledItem(T dueTime, IComparer<T> comparer)
		{
			this.DueTime = dueTime;
			if (comparer == null)
			{
				throw new ArgumentNullException("comparer");
			}
			this._comparer = comparer;
		}

		public T DueTime { get; }

		public void Invoke()
		{
			if (!Disposable.GetIsDisposed(ref this._disposable))
			{
				Disposable.SetSingle(ref this._disposable, this.InvokeCore());
			}
		}

		protected abstract IDisposable InvokeCore();

		public int CompareTo(ScheduledItem<T> other)
		{
			if (other == null)
			{
				return 1;
			}
			return this._comparer.Compare(this.DueTime, other.DueTime);
		}

		public static bool operator <(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return Comparer<ScheduledItem<T>>.Default.Compare(left, right) < 0;
		}

		public static bool operator <=(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return Comparer<ScheduledItem<T>>.Default.Compare(left, right) <= 0;
		}

		public static bool operator >(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return Comparer<ScheduledItem<T>>.Default.Compare(left, right) > 0;
		}

		public static bool operator >=(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return Comparer<ScheduledItem<T>>.Default.Compare(left, right) >= 0;
		}

		public static bool operator ==(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return left == right;
		}

		public static bool operator !=(ScheduledItem<T> left, ScheduledItem<T> right)
		{
			return !(left == right);
		}

		public override bool Equals(object obj)
		{
			return this == obj;
		}

		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		public void Cancel()
		{
			Disposable.TryDispose(ref this._disposable);
		}

		public bool IsCanceled
		{
			get
			{
				return Disposable.GetIsDisposed(ref this._disposable);
			}
		}

		void IDisposable.Dispose()
		{
			this.Cancel();
		}

		private IDisposable _disposable;

		private readonly IComparer<T> _comparer;
	}
}
