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

using System.Threading;

namespace Watki
{
    class Program
    {
        static Random r = new Random();
        static int ileWatkow = 100;
        static double pi = 0;

        static EventWaitHandle[] ewht = new EventWaitHandle[ileWatkow]; //obiekty synchronizacji

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

            //kontrola ilości instancji aplikacji
            bool pierwszaInstancja;
            Mutex m = new Mutex(true, "BardzoUnikalnaNazwaMuteksu", out pierwszaInstancja);
            if (!pierwszaInstancja)
            {
                Console.WriteLine("Instancja tego programu jest już uruchomiona!");
                return;
            }

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

            /*
            //czekanie na zakonczenie watkow
            int ileDostepnychWatkowWPuli = 0; //nieuzywane watki puli
            int ileWszystkichWatkowWPuli = 0; //wszystkie watki puli
            int ileDzialajacychWatkowPuli = 0; //uzywane watki puli
            int tmp = 0;
            do
            {
                ThreadPool.GetAvailableThreads(out ileDostepnychWatkowWPuli, out tmp);
                ThreadPool.GetMaxThreads(out ileWszystkichWatkowWPuli, out tmp);
                ileDzialajacychWatkowPuli = ileWszystkichWatkowWPuli - ileDostepnychWatkowWPuli;
                //Console.Clear();
                Console.WriteLine("Ilość aktywnych wątków puli: {0}", ileDzialajacychWatkowPuli);
                Thread.Sleep(1000);
            }
            while (ileDzialajacychWatkowPuli > 0);
            */

            for (int i = 0; i < ileWatkow; ++i) ewht[i].WaitOne();
            pi /= 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 double obliczPi(long ilośćPrób)
        {
            Random r=new Random(Program.r.Next() & DateTime.Now.Millisecond);
            double x, y;
            long ilośćTrafień = 0;
            for (int i = 0; i < ilośćPrób; ++i)
            {
                if (i == ilośćPrób / 2)
                {
                    /*lock ((object)Program.pi)
                    {
                        Console.WriteLine("Synchronizacja: watek nr {0} osiągnął półmetek", Thread.CurrentThread.ManagedThreadId);
                    }*/
                    /*Monitor.Enter((object)Program.pi);
                    try
                    {
                        Console.WriteLine("Synchronizacja: watek nr {0} osiągnął półmetek", Thread.CurrentThread.ManagedThreadId);
                    }
                    finally
                    {
                        Monitor.Exit((object)Program.pi);
                    }*/
                }
                x = r.NextDouble();
                y = r.NextDouble();
                if (x * x + y * y < 1) ++ilośćTrafień;
                //Console.WriteLine("x={0}, y={1}", x, y);
            }
            return 4.0 * ilośćTrafień / ilośćPrób;
        }

        static void uruchamianieObliczenPi(object parametr)
        {
            try
            {
                int? indeks = parametr as int?;
                Console.WriteLine("Uruchamianie obliczeń, watek nr {0}, indeks {1}...", Thread.CurrentThread.ManagedThreadId, indeks!=null?indeks.ToString():"---");
                double pi = obliczPi(ilośćPrób: 10000000L);
                //double pi = obliczPi(ilośćPrób: 1000L);
                Program.pi += pi;
                Console.WriteLine("Pi={0}, błąd={1}, wątek nr {2}, indeks {3}", pi, Math.Abs(Math.PI - pi), Thread.CurrentThread.ManagedThreadId, indeks != null ? indeks.ToString() : "---");
                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 + ")");
            }
        }
    }
}
