using System;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Subjects;
using System.Threading;
using System.Collections.Generic;

namespace RX_Sample._03_ReactiveX
{
    class Sample03
    {
        #region 01

        private void Select()
        {
            Observable
                .Range(0, 10)
                .Select(o => o * 10);
        }

        #endregion

        #region 02

        private void Scan()
        {
            Observable
                .Range(1, 10)
                .Scan(10, (acc, value) => acc + value);

            // [ 10, 11, 13, 16 ... 155 ]

            Observable
                .Range(1, 10)
                .Aggregate(10, (acc, value) => acc + value);

            // [155]
        }

        #endregion

        #region 03

        private void Filter()
        {
            Observable
                .Range(1, 10)
                .Where(o => o % 3 == 0);
        }

        #endregion

        #region 04
        private void Distinct()
        {
            var source = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 2, 2, 1, 1, 1 }.ToObservable();

            source.Distinct();
            // [1, 2, 3]

            source.DistinctUntilChanged();
            // [1, 2, 3, 2, 1]
        }
        #endregion

        #region 05

        private void Debounce()
        {
            IScheduler scheduler = null;
            Observable
                .Range(0, 10)
                .Throttle(TimeSpan.FromSeconds(2), scheduler);
        }

        #endregion

        #region 06
        private void Subject()
        {
            var behviorSubject = new BehaviorSubject<int>(1);

            using (var sub1 = behviorSubject.Subscribe(o => Console.WriteLine(o)))
            {
                using (var sub2 = behviorSubject.Subscribe(o => Console.Error.WriteLine(o)))
                {
                    behviorSubject.OnNext(2);
                    behviorSubject.OnNext(3);
                }
            }
        }

        private void SubjectOptimisation()
        {
            var observable = Observable
                .Range(0, 10)
                .Select(o =>
                {
                    Thread.Sleep(100);
                    return o;
                });

            var subject = new Subject<int>();
            var subjectSubscription = observable.Subscribe(subject);

            var subscriptions = new List<IDisposable>();
            for(int i = 0; i < 100; i++)
            {
                subscriptions.Add(subject.Subscribe(o => Console.WriteLine(o)));
            }
        }

        #endregion

        #region 07

        private void Merge()
        {
            var subject1 = new Subject<int>();
            var subject2 = new Subject<int>();

            var merged = subject1.Merge(subject2);

            subject1.OnNext(1);

            subject2.OnNext(2);

            subject1.OnNext(3);

            subject2.OnNext(4);
        }

        #endregion

        #region 08

        class SelectManyRoot
        {
            public IObservable<SelectManyConversation> Conversations { get; }
        }

        class SelectManyConversation
        {
            public IObservable<SelectManyMessage> Messages { get; }
        }

        class SelectManyMessage
        {

        }

        private void SelectMany()
        {
            new SelectManyRoot()
                .Conversations
                .SelectMany(o => o.Messages);
        }

        #endregion

        #region 09

        class SwitchCallManager
        {
            public IObservable<SwitchCall> ActiveCall { get; }
        }

        class SwitchCall
        {
            public IObservable<string> CallerName { get; }
        }

        public void Switch()
        {
            new SwitchCallManager()
                .ActiveCall
                .Select(o => o.CallerName)
                .Switch();
        }

        #endregion
    }
}
