using System;
using System.Collections.Generic;
using System.Linq;

namespace Rowa.Observables;

/// <summary>
/// Combines many single item observables into one observable that returns an array of T
/// Each time one of the source observable sends a value this observable emits an array of all the latest values
/// of all the sources, if a source never provided a value the default/null value will be sent instead for that observable.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CombineObservable<T> : IObservable<T[]>
{
    private readonly IObservable<T>[] _sources;
    private readonly T[] _values;

    public CombineObservable(params IObservable<T>[] sources)
    {
        _sources = sources;
        _values = new T[sources.Length];

    }

    public IDisposable Subscribe(IObserver<T[]> observer)
    {
        var observers = _sources.Select((x, i) =>  x.Subscribe(new DelegateObserver<T>( v =>
        {
            _values[i] = v;
            observer.OnNext(_values);
        }))).ToList();
        return new ListDisposable(observers);
    }

    private record ListDisposable(List<IDisposable> Disposables) : IDisposable
    {
        public void Dispose()
        {
            Disposables.ForEach(d => d.Dispose());
        }
    }
}