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;
        const long ilośćPróbWWątku = 10000000L;
        static long całkowitaIlośćTrafień = 0L;
        static long całkowitaIlośćPrób = 0L;
        static EventWaitHandle[] ewht = new EventWaitHandle[ileWatkow];

        #region Kolory
        static ConsoleColor[] kolory = new ConsoleColor[]
        {            
            ConsoleColor.Cyan,
            ConsoleColor.Green,
            ConsoleColor.Magenta,
            ConsoleColor.Red,
            ConsoleColor.Yellow
        };

        static ConsoleColor KolorWatku
        {
            get
            {
                return kolory[Thread.CurrentThread.ManagedThreadId % kolory.Length];
            }
        }

        static void ConsoleColorWriteLine(ConsoleColor foregroundColor, string format, params object[] arg)
        {
            lock (r)
            {
                Console.ForegroundColor = foregroundColor;
                Console.WriteLine(format, arg);
            }
        }
        #endregion

        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            //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, new object[] { i, ct });
            }

            Thread.Sleep(2000);
            cts.Cancel();

            for (int i = 0; i < ileWatkow; ++i) ewht[i].WaitOne();            
            double pi = 4.0 * całkowitaIlośćTrafień / (ilośćPróbWWątku*ileWatkow);
            ConsoleColorWriteLine(ConsoleColor.White, "Wszystkie wątki zakończyły działanie.\nUśrednione Pi={0}, błąd={1}", pi, Math.Abs(Math.PI - pi));
        }

        static long obliczPi(long ilośćPrób, CancellationToken ct)
        {
            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)
            {
                //wstrzymywanie watkow
                ct.ThrowIfCancellationRequested();

                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)
        {
            object[] parametry = (object[])parametr;
            int? indeks = parametry[0] as int?;
            try
            {                
                CancellationToken ct = (CancellationToken)parametry[1];
                ConsoleColorWriteLine(KolorWatku, "Uruchamianie obliczeń, watek nr {0}, indeks {1} ...", Thread.CurrentThread.ManagedThreadId, indeks.HasValue ? indeks.Value.ToString() : "---");

                long ilośćTrafień = obliczPi(ilośćPróbWWątku, ct);
                //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);                                
            }
            catch (ThreadAbortException exc)
            {
                ConsoleColorWriteLine(KolorWatku, "Działanie wątku zostało przerwane (" + exc.Message + ")");
            }
            catch (OperationCanceledException exc)
            {
                ConsoleColorWriteLine(KolorWatku, "Działanie wątku zostało anulowane (" + exc.Message + ")");
            }
            catch (Exception exc)
            {
                ConsoleColorWriteLine(KolorWatku, "Wyjątek (" + exc.Message + ")");
            }
            finally
            {
                ewht[indeks.Value].Set();
                ConsoleColorWriteLine(KolorWatku, "Zakończenie obliczeń, watek nr {0}, indeks {1}.", Thread.CurrentThread.ManagedThreadId, indeks.HasValue ? indeks.Value.ToString() : "---");
            }
        }
    }
}
