using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace nft.test.test.RxPractice
{
    public class HeavyTaskModel
    {
        const int MAX_PROCEED = 1000;
        private int proceed = 0;
        private bool debugLog = true;
        /// Rx式のイベントを実装する時は、実体はSubject,外部へのインターフェースはIObservableが良い
        private SubjectBase<int> notifyProgress;
        private string name = "";

        public HeavyTaskModel(string name, SubjectBase<int> subject = null)
        {
            this.notifyProgress = subject != null ? subject : new Subject<int>();
            this.name = name;
        }
        public HeavyTaskModel(SubjectBase<int> subject = null) : this("", subject)
        {
        }

        // 重い処理のイメージ
        public void Process()
        {
            string prefix = name.Length > 0 ? string.Format("[{0}] ", name) : "#";
            for (int i = 0; i < 10; i++)
            {
                if (debugLog) Debug.WriteLine("{0}{1}", prefix, i);
                notifyProgress.OnNext(i);
                Thread.Sleep(350);
            }
            notifyProgress.OnCompleted();
        }

        // 重い処理のイメージ
        public void Process2()
        {
            string prefix = name.Length > 0 ? string.Format("[{0}] ", name) : "";
            Debug.Assert(proceed == 0, prefix + "The process ALREADY Started!");
            Random rand = new Random();
            int vRange = MAX_PROCEED / 100;
            int aRange = MAX_PROCEED / 150;
            int velocity = rand.Next(2, vRange) + rand.Next(2, vRange);
            int accel = rand.Next(aRange) - rand.Next(aRange);
            int count = 0;
            if(debugLog) Debug.WriteLine("{0}Demo Process: proceed={1}, v={2}, a={3}", prefix, proceed, velocity, accel);
            while (proceed < MAX_PROCEED)
            {
                notifyProgress.OnNext(proceed);
                Thread.Sleep(20);
                count++;
                proceed += velocity;
                velocity += accel;
                if ( accel < 0 && velocity < vRange/2 || accel > 0 && velocity > vRange*3/2 || count % 100 == 0 )
                {
                    do
                    {
                        accel = rand.Next(aRange + Math.Max(vRange - velocity, 1)) - rand.Next(aRange + Math.Max(velocity - vRange, 0));
                    } while (accel < 0 && velocity < vRange / 2 || accel > 0 && velocity > vRange * 3 / 2);
                    if (debugLog) Debug.WriteLine("{0}Demo Process: proceed={1}, v={2}, a={3}", prefix, proceed, velocity, accel);
                }
            }
            notifyProgress.OnNext(MAX_PROCEED);
            notifyProgress.OnCompleted();
        }
        
        /// <summary>
        /// 進捗を通知するIObservable
        /// Rx式のイベントを実装する時は、実体はSubject,外部へのインターフェースはIObservableが良い
        /// </summary>                  
        public IObservable<int> NotifyProgress
        {
            get
            {
                return notifyProgress;
            }
        }
    }
}
