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

using System.Threading;

namespace ProducentKonsument
{
    class Program
    {        
        static object obiektSynchronizacjiMagazynu = new object();
        static object obiektSynchronizacjiProducenta = new object();
        static object obiektSynchronizacjiKonsumenta = new object();
        static Random r = new Random();

        static Task zadanieProducenta = null;
        static Task zadanieKonsumenta = null;

        const int maksymalnyCzasProdukcji = 1000;
        const int maksymalnyCzasKonsumpcji = 1000;
        const int maksymalnyCzasUruchomieniaProdukcji = 5000;
        const int maksymalnyCzasUruchomieniaKonsumpcji = 5000;

        static int pojemnoscMagazynu = 20;
        static int licznikElementowWMagazynie = 10;

        static void wyswietlStanMagazynu()
        {
            Console.WriteLine("Liczba elementów w magazynie: " + licznikElementowWMagazynie.ToString());
        }

        static void Main(string[] args)
        {
            Action akcjaProducenta =
                () =>
                {
                    Console.WriteLine("Zadanie producenta jest uruchamiane");
                    while (true)
                    {
                        lock (obiektSynchronizacjiMagazynu)
                        {
                            licznikElementowWMagazynie++;
                            Console.Write("Element dodany. ");
                        }
                        wyswietlStanMagazynu();
                        if (licznikElementowWMagazynie >= pojemnoscMagazynu)
                        {
                            Console.WriteLine("Zadanie producenta zostanie uśpione");
                            lock (obiektSynchronizacjiProducenta) Monitor.Wait(obiektSynchronizacjiProducenta);
                            Console.WriteLine("Zadanie producenta zostanie wznowione");
                            Thread.Sleep(r.Next(maksymalnyCzasUruchomieniaProdukcji));                            
                            Console.WriteLine("Zadanie producenta zostało wznowione");
                        }
                        lock (obiektSynchronizacjiKonsumenta) Monitor.Pulse(obiektSynchronizacjiKonsumenta);
                        Thread.Sleep(r.Next(maksymalnyCzasProdukcji));
                    }
                };

            Action akcjaKonsumenta =
                () =>
                {
                    Console.WriteLine("Zadanie konsumenta jest uruchamiane");
                    while (true)
                    {
                        lock (obiektSynchronizacjiMagazynu)
                        {
                            licznikElementowWMagazynie--;
                            Console.Write("Element zabrany. ");
                        }
                        wyswietlStanMagazynu();
                        if (licznikElementowWMagazynie <= 0)
                        {
                            Console.WriteLine("Zadanie konsumenta zostanie uśpione");
                            lock (obiektSynchronizacjiKonsumenta) Monitor.Wait(obiektSynchronizacjiKonsumenta);
                            Console.WriteLine("Zadanie konsumenta zostanie wznowione");
                            Thread.Sleep(r.Next(maksymalnyCzasUruchomieniaKonsumpcji));
                            Console.WriteLine("Zadanie konsumenta zostało wznowione");
                        }
                        lock (obiektSynchronizacjiProducenta) Monitor.Pulse(obiektSynchronizacjiProducenta);
                        Thread.Sleep(r.Next(maksymalnyCzasKonsumpcji));
                    }
                };

            zadanieProducenta = new Task(akcjaProducenta);            
            zadanieProducenta.Start();             

            zadanieKonsumenta = new Task(akcjaKonsumenta);            
            zadanieKonsumenta.Start();            

            Console.ReadLine();
            Console.Write("Koniec. "); wyswietlStanMagazynu();
        }
    }
}
