// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Reactive.Disposables;

namespace System.Reactive
{
    //
    // See AutoDetachObserver.cs for more information on the safeguarding requirement and
    // its implementation aspects.
    //

    internal abstract class SafeObserver<TSource> : ISafeObserver<TSource>
    {
        private sealed class WrappingSafeObserver : SafeObserver<TSource>
        {
            private readonly IObserver<TSource> _observer;

            public WrappingSafeObserver(IObserver<TSource> observer)
            {
                _observer = observer;
            }

            public override void OnNext(TSource value)
            {
                var noError = false;
                try
                {
                    _observer.OnNext(value);
                    noError = true;
                }
                finally
                {
                    if (!noError)
                    {
                        Dispose();
                    }
                }
            }

            public override void OnError(Exception error)
            {
                using (this)
                {
                    _observer.OnError(error);
                }
            }

            public override void OnCompleted()
            {
                using (this)
                {
                    _observer.OnCompleted();
                }
            }
        }

        public static ISafeObserver<TSource> Wrap(IObserver<TSource> observer)
        {
            if (observer is AnonymousObserver<TSource> a)
            {
                return a.MakeSafe();
            }

            return new WrappingSafeObserver(observer);
        }

        private SingleAssignmentDisposableValue _disposable;

        public abstract void OnNext(TSource value);

        public abstract void OnError(Exception error);

        public abstract void OnCompleted();

        public void SetResource(IDisposable resource)
        {
            _disposable.Disposable = resource;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _disposable.Dispose();
            }
        }
    }
}
