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

using System.Threading.Tasks;
using System.Threading;

namespace Zadania
{
    class Program
    {
        static void TworzenieZadania()
        {
            Task t = new Task(() => { Console.WriteLine("Dzień dobry!"); });
            t.Start();
            t.Wait();
        }

        static void PracaZZadaniami()
        {
            Action a = () =>
            {
                Console.WriteLine("Start zadania nr " + Task.CurrentId);
                Thread.SpinWait(new Random().Next(100000000));
                Console.WriteLine("Koniec zadania nr " + Task.CurrentId);
            };

            //kod tworzący wątki, uruchamiający i czekający na ich zakończenie
            List<Task> listaZadan = new List<Task>();

            for (int i = 0; i < 100; i++)
            {
                listaZadan.Add(new Task(a));
            }

            listaZadan.ForEach(t => t.Start());
            listaZadan.ForEach(t => t.Wait());
        }

        static void DanePrzekazywaneDoZadan()
        {
            Task t = new Task((o) => { Console.WriteLine(o.ToString()); }, "Dzień dobry!");
            t.Start();
            t.Wait();
        }

        static void DaneZwracanePrzezZadanie()
        {
            Task<String> t = new Task<String>(() => { return "Dzień dobry!"; });

            t.Start();
            t.Wait();
            Console.WriteLine(t.Result);
        }

        static void TestLiczbyPierwszej()
        {
            List<Task<int>> lista = new List<Task<int>>();
            Console.Write("Sprawdź, czy liczba pierwsza: ");
            int n = Int32.Parse(Console.ReadLine());

            for (int i = 2; i < (int)Math.Sqrt(n); i++)
            {
                lista.Add(new Task<int>((j) =>
                {
                    if (n % (int)j == 0)
                    {
                        return (int)j;
                    }
                    else
                    {
                        return 0;
                    }
                }, i));
            }

            foreach (Task<int> t in lista) { t.Start(); }

            foreach (Task<int> t in lista) { t.Wait(); }

            bool pierwsza = true;
            foreach (Task<int> t in lista)
            {
                if (t.Result != 0)
                {
                    Console.WriteLine("Liczba {0} dzieli się przez {1}.", n, t.Result);
                    pierwsza = false;
                }
            }
            if (pierwsza) Console.WriteLine("Liczba {0} jest liczbą pierwszą.", n);
        }

        static void SynchronizacjaZadan_ContinueWith()
        {
            Task t1, t2, t3;

            t1 = new Task(() => { Thread.Sleep(1000); Console.WriteLine("zadanie t1 o identyfikatorze {0} zakończone", Task.CurrentId); });
            t2 = new Task(() => { Thread.Sleep(2000); Console.WriteLine("zadanie t2 o identyfikatorze {0} zakończone", Task.CurrentId); });
            t3 = new Task(() => { Thread.Sleep(3000); Console.WriteLine("zadanie t3 o identyfikatorze {0} zakończone", Task.CurrentId); });

            Task[] zadania = { t1, t2, t3 };

            t2.ContinueWith((t) => { Console.WriteLine("Zadanie o identyfikatorze {1} zostało wykonane po zakończeniu zadania t2 o identyfikatorze {0}", t.Id, Task.CurrentId); } );

            foreach (Task t in zadania) t.Start();
            foreach (Task t in zadania) t.Wait();
        }

        static void SztafetaWatkow()
        {
            Task t1, t2, t3, t4;
            Action a = () =>
            {
                Console.WriteLine("Zawodnik nr {0} wystartował", Task.CurrentId);
                Thread.Sleep(new Random().Next(1000, 1500));
                Console.WriteLine("Zawodnik nr {0} zakończył bieg", Task.CurrentId);
            };

            Action<Task> b = (t) =>
            {
                Console.WriteLine("Zawodnik nr {0} wystartował po zawodniku nr {1}", Task.CurrentId, t.Id);
                Thread.Sleep(new Random().Next(1000, 2500));
                Console.WriteLine("Zawodnik nr {0} zakończył bieg", Task.CurrentId);
            };

            t1 = new Task(a);
            t2 = new Task(a);
            t3 = t1.ContinueWith(b);
            t4 = t2.ContinueWith(b);

            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2, t3, t4);
            Console.WriteLine("Wyścig zakończony");
        }

        static void PrzerywanieZadan()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            Task t = new Task(() =>
            {
                try
                {
                    Console.WriteLine("Zadanie zostało uruchomione");
                    for (; ; )
                    {
                        ct.ThrowIfCancellationRequested();
                    }
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Zadanie zostało przerwane");
                }
            }, ct);

            t.Start();

            Thread.Sleep(3000);
            cts.Cancel();
            t.Wait();
        }

        static void PrzerywanieZadan_Wait()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            Task t = new Task(() =>
            {
                Console.WriteLine("Zadanie zostało uruchomione");
                for (; ; )
                {
                    Thread.Sleep(3000);
                    cts.Cancel();
                }
            });

            t.Start();
            Task[] zadania = { t };

            try
            {
                Task.WaitAll(zadania, ct);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Przerwano oczekiwanie");
            }
        }

        static void StanZadania()
        {
            Task test = new Task(() =>
            {
                Console.WriteLine("Zadanie rozpoczęte");
                Thread.Sleep(200);
                Console.WriteLine("Zadanie zakończone");
            });

            //zadanie sprawdzajace co chwila w jakim stanie jest zadanie testowe
            Task obserwator = Task.Factory.StartNew(() =>
                {
                    int i = 10;
                    while (i-- > 0)
                    {
                        Thread.Sleep(100);
                        Console.WriteLine(test.Status);
                    }
                });

            Thread.Sleep(200);
            test.Start();

            Task.WaitAll(test, obserwator);
        }

        static void StanZadania_PrzerwanieDwochZadanZUwzglednieniemZmianyStanu()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            Task test1 = new Task(() =>
            {
                Console.WriteLine("\nStart zadania (id:{0})\n", Task.CurrentId);
                while (true)
                {
                    ct.ThrowIfCancellationRequested();
                }
            }, ct, TaskCreationOptions.LongRunning);

            Task test2 = new Task(() =>
            {
                Console.WriteLine("\nStart zadania (id:{0})\n", Task.CurrentId);
                while (true)
                {
                    ct.ThrowIfCancellationRequested();
                }
            }, TaskCreationOptions.LongRunning);

            //zadanie sprawdzajace co chwila w jakim stanie jest zadanie testowe
            Task obserwator = Task.Factory.StartNew(() =>
                {
                    while (!(test1.IsCompleted && test2.IsCompleted))
                    {
                        Thread.Sleep(100);
                        Console.WriteLine("Test1: {0}\t\tTest2: {1}", test1.Status, test2.Status);
                    }
                });

            Thread.Sleep(200);
            test1.Start();
            test2.Start();

            while (test1.Status != TaskStatus.Running || test2.Status != TaskStatus.Running) ;
            Thread.Sleep(200);
            Console.WriteLine("\nPrzerwanie\n");
            cts.Cancel();

            try
            {
                Task.WaitAll(test1, test2, obserwator);
            }
            catch (AggregateException ae)
            {
                foreach (var exc in ae.InnerExceptions)
                {
                    Console.WriteLine("Przechwycono wyjątek: {0}", exc.Message);
                }
            }

            Console.WriteLine("Naciśnij dowolny klawisz...");
            Console.ReadKey();
        }

        static void FabrykaZadan()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            Task<int> t2 = Task<int>.Factory.StartNew(
                (o) => { Console.WriteLine(o.ToString());  return 1; },
                (object)"Dzień dobry!",
                ct,
                TaskCreationOptions.None,
                TaskScheduler.Default
            );            
            t2.Wait();
        }

        static void FabrykaZadan_SztafetaZadan()
        {
            Task t1, t2, t3, t4;
            Action a = () =>
            {
                Console.WriteLine("Zawodnik nr {0} wystartował", Task.CurrentId);
                Thread.Sleep(new Random().Next(1000, 1500));
                Console.WriteLine("Zawodnik nr {0} zakończył bieg", Task.CurrentId);
            };

            Action<Task> b = (t) =>
            {
                Console.WriteLine("Zawodnik nr {0} wystartował po zawodniku nr {1}", Task.CurrentId, t.Id);
                Thread.Sleep(new Random().Next(1000, 2500));
                Console.WriteLine("Zawodnik nr {0} zakończył bieg", Task.CurrentId);
            };

            t3 = (t1 = Task.Factory.StartNew(a)).ContinueWith(b);
            t4 = (t2 = Task.Factory.StartNew(a)).ContinueWith(b);

            Task[] zadania = { t3, t4 };
            Task.Factory.ContinueWhenAny(zadania, (t) =>
                {
                    Console.WriteLine("Zawodnik nr {0} wygrał wyścig!", t.Id);
                });
            Task.WaitAll(zadania);
            Console.WriteLine("Wyścig zakończony");
        }

        static void FabrykaZadan_WlasnaFabryka()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;

            TaskFactory tf = new TaskFactory(ct);

            Task a = tf.StartNew(() =>
            {
                while (true)
                {
                    Thread.Sleep(500);
                    ct.ThrowIfCancellationRequested();
                }
            });

            cts.Cancel();

            try
            {
                a.Wait();
            }
            catch (AggregateException)
            {
                Console.WriteLine("Koniec!");
            }
        }

        class Planista : TaskScheduler
        {
            private List<Task> kolejka = new List<Task>();
            private Thread watekGlowny;

            public Planista()
            {
                watekGlowny = new Thread(() =>
                    {
                        Console.WriteLine("Planista utworzony.");
                        while (true)
                        {
                            if (kolejka.Count > 0)
                            {
                                int i = new Random().Next(kolejka.Count);

                                if (TryExecuteTask(kolejka[i])) kolejka.RemoveAt(i);
                            }
                        }
                    });

                watekGlowny.IsBackground = true;
                watekGlowny.Start();
            }

            public override int MaximumConcurrencyLevel
            {
                get
                {
                    return 1;
                }
            }

            protected override void QueueTask(Task task)
            {
                kolejka.Add(task);
            }

            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
            {
                Console.WriteLine("żądanie wykonania sekwencyjnego");
                if (Thread.CurrentThread != watekGlowny) return false;
                return TryExecuteTask(task);
            }

            protected override IEnumerable<Task> GetScheduledTasks()
            {
                return kolejka.ToArray();
            }
        }

        static void Planista_Uzycie()
        {
            Planista ts = new Planista();
            TaskFactory tf = new TaskFactory(ts);

            Task[] zadania = new Task[10];
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("zadanie {0} rozpoczęte", i + 1);
                zadania[i] = tf.StartNew((numer) =>
                {
                    Console.WriteLine("Zadanie {0} zakończone", numer);
                }, i + 1
                );
            }
            Task.WaitAll(zadania, 20000);

            Console.WriteLine("Naciśnij dowolny klawisz");
            Console.ReadKey();
        }

        static void Main(string[] args)
        {
            //TworzenieZadania();
            //PracaZZadaniami();
            //DanePrzekazywaneDoZadan();
            //DaneZwracanePrzezZadanie();
            //TestLiczbyPierwszej();
            //SynchronizacjaZadan_ContinueWith();
            //SztafetaWatkow();
            //PrzerywanieZadan();
            //PrzerywanieZadan_Wait();
            //StanZadania();
            //StanZadania_PrzerwanieDwochZadanZUwzglednieniemZmianyStanu();
            //FabrykaZadan();
            //FabrykaZadan_SztafetaZadan();
            //FabrykaZadan_WlasnaFabryka();
            Planista_Uzycie();
        }
    }
}
