using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Threading;

namespace Watki
{
    class Program
    {
        static Random r = new Random();        
        const int ileWatkow = 100;
        //static double pi = 0;
        const long ilośćPróbWWątku = 100000000L;
        static long całkowitaIlośćTrafień = 0L;
        static long całkowitaIlośćPrób = 0L;

        static EventWaitHandle[] ewht = new EventWaitHandle[ileWatkow];

        static void Main(string[] args)
        {
            int czasPoczatkowy = Environment.TickCount;

            //watek sprawozdawczy
            /*
            Timer timer = new Timer(
                (object state) =>
                {
                    Console.WriteLine("Ilość prób: " + Interlocked.Read(ref całkowitaIlośćPrób).ToString() + "/" + (ileWatkow * ilośćPróbWWątku).ToString());
                },
                null,
                0,
                1000);
            */

            /*
            System.Timers.Timer timer = new System.Timers.Timer(1000);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(
                (object sender, System.Timers.ElapsedEventArgs e)
                    =>
                {
                    Console.WriteLine("Ilość prób: " + Interlocked.Read(ref całkowitaIlośćPrób).ToString() + "/" + (ileWatkow * ilośćPróbWWątku).ToString());
                });
            timer.Start();
            */

            Thread watekAlaTimer = new Thread(
                ()=>
                    {
                        Console.WriteLine("Uruchamiam wątek sprawozdawczy");
                        try
                        {
                            while (true)
                            {
                                Thread.Sleep(1000);
                                Console.WriteLine("Ilość prób: " + Interlocked.Read(ref całkowitaIlośćPrób).ToString() + "/" + (ileWatkow * ilośćPróbWWątku).ToString());
                            }
                        }
                        catch (ThreadAbortException exc)
                        {                            
                            Console.WriteLine("Przerywanie działania wątku sprawozdawczego.\nKońcowa ilość prób: " + Interlocked.Read(ref całkowitaIlośćPrób).ToString() + "/" + (ileWatkow * ilośćPróbWWątku).ToString());
                        }
                    });
            watekAlaTimer.Priority = ThreadPriority.Highest;
            watekAlaTimer.IsBackground = true;
            watekAlaTimer.Start();

            //tworzenie watkow
            ThreadPool.SetMaxThreads(30, 100);
            WaitCallback metodaWatku = uruchamianieObliczenPi;
            for (int i = 0; i < ileWatkow; ++i)
            {
                ewht[i] = new EventWaitHandle(false, EventResetMode.AutoReset);
                ThreadPool.QueueUserWorkItem(metodaWatku, i);
            }

            for (int i = 0; i < ileWatkow; ++i) ewht[i].WaitOne();            
            //timer.Change(-1, System.Threading.Timeout.Infinite); timer.Dispose();
            //timer.Stop(); timer.Dispose();
            watekAlaTimer.Abort();
            double pi = 4.0 * całkowitaIlośćTrafień / (ilośćPróbWWątku*ileWatkow);
            Console.WriteLine("Wszystkie wątki zakończyły działanie.\nUśrednione Pi={0}, błąd={1}", pi, Math.Abs(Math.PI - pi));

            int czasKoncowy = Environment.TickCount;
            int roznica = czasKoncowy - czasPoczatkowy;
            Console.WriteLine("Czas obliczeń: " + (roznica).ToString());
        }

        static long obliczPi(long ilośćPrób)
        {
            Random r = new Random(Program.r.Next() & DateTime.Now.Millisecond);
            double x, y;
            long ilośćTrafień = 0;
            for (long i = 0; i < ilośćPrób; ++i)
            {
                x = r.NextDouble();
                y = r.NextDouble();
                if (x * x + y * y < 1) ++ilośćTrafień;
                Interlocked.Increment(ref całkowitaIlośćPrób);
                //Console.WriteLine("x={0}, y={1}", x, y);
            }
            return ilośćTrafień;
        }

        static void uruchamianieObliczenPi(object parametr)
        {
            try
            {                
                int? indeks = parametr as int?;
                Console.WriteLine("Uruchamianie obliczeń, watek nr {0}, indeks {1} ...", Thread.CurrentThread.ManagedThreadId, indeks.HasValue?indeks.Value.ToString():"---");
                
                long ilośćTrafień = obliczPi(ilośćPrób: ilośćPróbWWątku);
                //Program.pi += pi;
                Interlocked.Add(ref całkowitaIlośćTrafień, ilośćTrafień);
                //Console.WriteLine("Pi={0}, błąd={1}, wątek nr {2}", pi, Math.Abs(Math.PI - pi), Thread.CurrentThread.ManagedThreadId);
                ewht[indeks.Value].Set();
            }
            catch (ThreadAbortException exc)
            {
                Console.WriteLine("Działanie wątku zostało przerwane (" + exc.Message + ")");
            }
            catch (Exception exc)
            {
                Console.WriteLine("Wyjątek (" + exc.Message + ")");
            }
        }
    }
}
