using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Rowa.Observables;

public class Subject<T> : IObservable<T>
{
    /// <summary>
    /// Gets and sets the current value of the Observable,
    /// Setting the value will trigger the OnNext method of all currently subscribed Observers
    /// </summary>
    [MaybeNull]
    public T Current
    {
        get => _current;
        set
        {
            _current = value;
            foreach (var subjectSubscriptions in _activeSubscriptions)
            {
                subjectSubscriptions.Observer.OnNext(_current);
            }
        }
    }

    private readonly List<SubjectSubscription> _activeSubscriptions =
        new List<SubjectSubscription>();

    [MaybeNull]
    private T _current;

    public IDisposable Subscribe(IObserver<T> observer)
    {
        var subscription = new SubjectSubscription(observer, this);
        _activeSubscriptions.Add(subscription);
        observer.OnNext(_current);
        return subscription;
    }

    private record SubjectSubscription(
        IObserver<T> Observer,
        Subject<T> Source) : IDisposable
    {
        public void Dispose()
        {
            Source._activeSubscriptions.Remove(this);
        }
    }

    public void Complete()
    {
        // local copy to allow disposing of elements in OnComplete
        var observers = _activeSubscriptions.ToArray();
        foreach (var subscription in observers)
        {
            subscription.Observer.OnCompleted();
        }
    }

    public void SetError(Exception ex)
    {
        // local copy to allow disposing of elements in SetError
        var observer = _activeSubscriptions.ToArray();
        foreach (var subjectSubscription in observer)
        {
            subjectSubscription.Observer.OnError(ex);
        }
    }
}