using System;
using System.Collections.Generic;

namespace System.Reactive
{
	public abstract class EventPatternSourceBase<TSender, T>
	{
		protected EventPatternSourceBase(IObservable<EventPattern<TSender, T>> source, Action<Action<TSender, T>, EventPattern<TSender, T>> invokeHandler)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			this._source = source;
			if (invokeHandler == null)
			{
				throw new ArgumentNullException("invokeHandler");
			}
			this._invokeHandler = invokeHandler;
			this._subscriptions = new Dictionary<Delegate, Stack<IDisposable>>();
		}

		protected void Add(Delegate handler, Action<TSender, T> invoke)
		{
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			if (invoke == null)
			{
				throw new ArgumentNullException("invoke");
			}
			EventPatternSourceBase<TSender, T>.Observer observer = new EventPatternSourceBase<TSender, T>.Observer(this, handler, invoke);
			observer.SetResource(this._source.Subscribe(observer));
		}

		private void Add(Delegate handler, IDisposable disposable)
		{
			Dictionary<Delegate, Stack<IDisposable>> subscriptions = this._subscriptions;
			lock (subscriptions)
			{
				Stack<IDisposable> stack;
				if (!this._subscriptions.TryGetValue(handler, out stack))
				{
					stack = (this._subscriptions[handler] = new Stack<IDisposable>());
				}
				stack.Push(disposable);
			}
		}

		protected void Remove(Delegate handler)
		{
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			IDisposable disposable = null;
			Dictionary<Delegate, Stack<IDisposable>> subscriptions = this._subscriptions;
			lock (subscriptions)
			{
				Stack<IDisposable> stack;
				if (this._subscriptions.TryGetValue(handler, out stack))
				{
					disposable = stack.Pop();
					if (stack.Count == 0)
					{
						this._subscriptions.Remove(handler);
					}
				}
			}
			if (disposable != null)
			{
				disposable.Dispose();
			}
		}

		private readonly IObservable<EventPattern<TSender, T>> _source;

		private readonly Dictionary<Delegate, Stack<IDisposable>> _subscriptions;

		private readonly Action<Action<TSender, T>, EventPattern<TSender, T>> _invokeHandler;

		private sealed class Observer : ObserverBase<EventPattern<TSender, T>>, ISafeObserver<EventPattern<TSender, T>>, IObserver<EventPattern<TSender, T>>, IDisposable
		{
			public Observer(EventPatternSourceBase<TSender, T> sourceBase, Delegate handler, Action<TSender, T> invoke)
			{
				this._handler = handler;
				this._invoke = invoke;
				this._sourceBase = sourceBase;
			}

			protected override void OnNextCore(EventPattern<TSender, T> value)
			{
				this._sourceBase._invokeHandler(this._invoke, value);
			}

			protected override void OnErrorCore(Exception error)
			{
				this.Remove();
				error.Throw();
			}

			protected override void OnCompletedCore()
			{
				this.Remove();
			}

			private void Remove()
			{
				object gate = this._gate;
				lock (gate)
				{
					if (this._isAdded)
					{
						this._sourceBase.Remove(this._handler);
					}
					else
					{
						this._isDone = true;
					}
				}
			}

			public void SetResource(IDisposable resource)
			{
				object gate = this._gate;
				lock (gate)
				{
					if (!this._isDone)
					{
						this._sourceBase.Add(this._handler, resource);
						this._isAdded = true;
					}
				}
			}

			private bool _isDone;

			private bool _isAdded;

			private readonly Delegate _handler;

			private readonly object _gate = new object();

			private readonly Action<TSender, T> _invoke;

			private readonly EventPatternSourceBase<TSender, T> _sourceBase;
		}
	}
}
