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

namespace ImplementingEventsWithDelegates
{
	// Klasa służąca do przechowywania informacji o zdarzeniu
	// w tym przypadku zawiera jedynie dane dostępne w 
	// klasie Clock, ale można także przechowywać 
	// dodatkowe informacje o stanie
	public class TimeInfoEventArgs : EventArgs
	{
		public TimeInfoEventArgs(int hour, int minute, int second)
		{
			this.hour = hour;
			this.minute = minute;
			this.second = second;
		}
		public readonly int hour;
		public readonly int minute;
		public readonly int second;
	}

	// Podmiot programu — to tę klasę obserwują inne klasy.
	// Ta klasa publikuje jednego delegata:
	// SecondChanged
	public class Clock
	{
		private int hour;
		private int minute;
		private int second;

		// Delegat, którego trzeba zaimplementować w klasach subskrybujących
		public delegate void SecondChangeHandler
		   (
		   object clock,
		   TimeInfoEventArgs timeInformation
		   );

		// Egzemplarz powyższego delegata
		public SecondChangeHandler SecondChanged;

		// Uruchamianie zegara.
		// Zegar powoduje zgłaszanie zdarzenia co sekundę
		public void Run()
		{

			for (; ; )
			{
				// Uśpienie wątku na 10 milisekund
				Thread.Sleep(10);

				// Pobieranie bieżącego czasu
				System.DateTime dt = System.DateTime.Now;
				// Jeśli sekunda zmieniła się,
				// należy powiadomić subskrybentów
				if (dt.Second != second)
				{
					// Tworzenie obiektu typu TimeInfoEventArgs 
					// przekazywanego subskrybentom
					TimeInfoEventArgs timeInformation =
					   new TimeInfoEventArgs(
						  dt.Hour, dt.Minute, dt.Second);

					// Jeśli istnieją subskrybenty, należy je poinformować
					if (SecondChanged != null)
					{
						SecondChanged(
						this, timeInformation);
					}
				}

				// Aktualizacja stanu
				this.second = dt.Second;
				this.minute = dt.Minute;
				this.hour = dt.Hour;

			}
		}
	}

	// Obserwator. Klasa DisplayClock subskrybuje
	// zdarzenia zegara. Zadaniem obiektu tej klasy 
	// jest wyświetlanie bieżącego czasu
	public class DisplayClock
	{
		// Przyjmuje zegar i subskrybuje
		// jego zdarzenie, SecondChangeHandler
		public void Subscribe(Clock theClock)
		{
			theClock.SecondChanged +=
			   new Clock.SecondChangeHandler(TimeHasChanged);
		}

		// Metoda zawierająca 
		// delegowaną funkcjonalność
		public void TimeHasChanged(
		   object theClock, TimeInfoEventArgs ti)
		{
			Console.WriteLine("Bieżący czas: {0}:{1}:{2}",
			   ti.hour.ToString(),
			   ti.minute.ToString(),
			   ti.second.ToString());
		}
	}
	// Drugi subskrybent, zapisujący informacje do pliku
	public class LogCurrentTime
	{
		public void Subscribe(Clock theClock)
		{
			theClock.SecondChanged +=
			   new Clock.SecondChangeHandler(WriteLogEntry);
		}

		// Te metoda powinna zapisywać informacje do pliku.
		// Tu wyświetla je w oknie konsoli, co pozwala zobaczyć, 
		// że obiekt nie przechowuje stanu
		public void WriteLogEntry(
		   object theClock, TimeInfoEventArgs ti)
		{
			Console.WriteLine("Zapis do pliku: {0}:{1}:{2}",
			   ti.hour.ToString(),
			   ti.minute.ToString(),
			   ti.second.ToString());
		}
	}

	public class Test
	{
		public static void Main()
		{
			// Tworzy nowy zegar
			Clock theClock = new Clock();

			// Tworzy obiekt klasy DisplayClock i nakazuje
			// subskrypcję zdarzeń utworzonego wcześniej zegara
			DisplayClock dc = new DisplayClock();
			dc.Subscribe(theClock);

			// Tworzy obiekt klasy LogCurrentTime i nakazuje
			// subskrypcję zdarzeń zegara
			LogCurrentTime lct = new LogCurrentTime();
			lct.Subscribe(theClock);

			// Uruchamia zegar
			theClock.Run();
		}
	}
}
