using System;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Runtime.CompilerServices;
using System.Threading;

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class RefCount<TSource>
	{
		internal sealed class Eager : Producer<TSource, RefCount<TSource>.Eager._>
		{
			public Eager(IConnectableObservable<TSource> source)
			{
				this._source = source;
				this._gate = new object();
			}

			protected override RefCount<TSource>.Eager._ CreateSink(IObserver<TSource> observer)
			{
				return new RefCount<TSource>.Eager._(observer, this);
			}

			protected override void Run(RefCount<TSource>.Eager._ sink)
			{
				sink.Run();
			}

			private readonly IConnectableObservable<TSource> _source;

			private readonly object _gate;

			private RefCount<TSource>.Eager.RefConnection _connection;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(IObserver<TSource> observer, RefCount<TSource>.Eager parent)
					: base(observer)
				{
					this._parent = parent;
				}

				public void Run()
				{
					bool flag = false;
					RefCount<TSource>.Eager.RefConnection refConnection = null;
					object gate = this._parent._gate;
					lock (gate)
					{
						refConnection = this._parent._connection;
						if (refConnection == null)
						{
							refConnection = new RefCount<TSource>.Eager.RefConnection();
							this._parent._connection = refConnection;
						}
						RefCount<TSource>.Eager.RefConnection refConnection2 = refConnection;
						int count = refConnection2._count;
						refConnection2._count = count + 1;
						flag = count == 0;
						this._targetConnection = refConnection;
					}
					this.Run(this._parent._source);
					if (flag && !Disposable.GetIsDisposed(ref refConnection._disposable))
					{
						Disposable.SetSingle(ref refConnection._disposable, this._parent._source.Connect());
					}
				}

				protected override void Dispose(bool disposing)
				{
					base.Dispose(disposing);
					if (disposing)
					{
						RefCount<TSource>.Eager.RefConnection targetConnection = this._targetConnection;
						this._targetConnection = null;
						object gate = this._parent._gate;
						lock (gate)
						{
							if (targetConnection == this._parent._connection)
							{
								RefCount<TSource>.Eager.RefConnection refConnection = targetConnection;
								int num = refConnection._count - 1;
								refConnection._count = num;
								if (num == 0)
								{
									this._parent._connection = null;
									goto IL_0069;
								}
							}
							return;
						}
						IL_0069:
						Disposable.TryDispose(ref targetConnection._disposable);
					}
				}

				private readonly RefCount<TSource>.Eager _parent;

				private RefCount<TSource>.Eager.RefConnection _targetConnection;
			}

			private sealed class RefConnection
			{
				internal int _count;

				internal IDisposable _disposable;
			}
		}

		internal sealed class Lazy : Producer<TSource, RefCount<TSource>.Lazy._>
		{
			public Lazy(IConnectableObservable<TSource> source, TimeSpan disconnectTime, IScheduler scheduler)
			{
				this._source = source;
				this._gate = new object();
				this._disconnectTime = disconnectTime;
				this._scheduler = scheduler;
			}

			protected override RefCount<TSource>.Lazy._ CreateSink(IObserver<TSource> observer)
			{
				return new RefCount<TSource>.Lazy._(observer);
			}

			protected override void Run(RefCount<TSource>.Lazy._ sink)
			{
				sink.Run(this);
			}

			private readonly object _gate;

			private readonly IScheduler _scheduler;

			private readonly TimeSpan _disconnectTime;

			private readonly IConnectableObservable<TSource> _source;

			private IDisposable _serial;

			private int _count;

			private IDisposable _connectableSubscription;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(IObserver<TSource> observer)
					: base(observer)
				{
				}

				public void Run(RefCount<TSource>.Lazy parent)
				{
					IDisposable disposable = parent._source.SubscribeSafe(this);
					object gate = parent._gate;
					lock (gate)
					{
						int num = parent._count + 1;
						parent._count = num;
						if (num == 1)
						{
							if (parent._connectableSubscription == null)
							{
								parent._connectableSubscription = parent._source.Connect();
							}
							Disposable.TrySetSerial(ref parent._serial, new SingleAssignmentDisposable());
						}
					}
					base.SetUpstream(Disposable.Create<ValueTuple<RefCount<TSource>.Lazy, IDisposable>>(new ValueTuple<RefCount<TSource>.Lazy, IDisposable>(parent, disposable), delegate([TupleElementNames(new string[] { "parent", "subscription" })] ValueTuple<RefCount<TSource>.Lazy, IDisposable> tuple)
					{
						RefCount<TSource>.Lazy item = tuple.Item1;
						tuple.Item2.Dispose();
						object gate2 = item._gate;
						lock (gate2)
						{
							RefCount<TSource>.Lazy lazy = item;
							int num2 = lazy._count - 1;
							lazy._count = num2;
							if (num2 == 0)
							{
								SingleAssignmentDisposable singleAssignmentDisposable = (SingleAssignmentDisposable)Volatile.Read<IDisposable>(ref item._serial);
								singleAssignmentDisposable.Disposable = item._scheduler.ScheduleAction(new ValueTuple<SingleAssignmentDisposable, RefCount<TSource>.Lazy>(singleAssignmentDisposable, item), item._disconnectTime, delegate([TupleElementNames(new string[] { "cancelable", "closureParent" })] ValueTuple<SingleAssignmentDisposable, RefCount<TSource>.Lazy> tuple2)
								{
									object gate3 = tuple2.Item2._gate;
									lock (gate3)
									{
										if (Volatile.Read<IDisposable>(ref tuple2.Item2._serial) == tuple2.Item1)
										{
											tuple2.Item2._connectableSubscription.Dispose();
											tuple2.Item2._connectableSubscription = null;
										}
									}
								});
							}
						}
					}));
				}
			}
		}
	}
}
