//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Diagnostics;

namespace DiningPhilosophers
{
	public class DiningPhilosopher : System.Windows.Forms.Button
	{
		private Mutex [] chopsticks;
		private Thread stateHandler;
		// Zdefiniowanie specjalnego delegata, który obsługuje szeregowanie
		// informacji o zmianie koloru z wątku pracującego w tle
		// do wątku zawierającego przycisk.
		private delegate void ColorChangeDelegate(Color color);
		private ColorChangeDelegate colorChangeDelegate;
		private EventHandler onPhilosopherLeave;
		public DiningPhilosopher(Mutex rightChopstick, Mutex leftChopstick)
		{
			chopsticks = new Mutex[2];
			chopsticks[0] = rightChopstick;
			chopsticks[1] = leftChopstick;

			colorChangeDelegate = new ColorChangeDelegate(ColorChange);
			onPhilosopherLeave = new EventHandler(OnPhilosopherLeave);
			stateHandler = null;
		}
		public void Start()
		{
			stateHandler = new Thread(new ThreadStart(StateHandler));
			if(base.Name != null)
				stateHandler.Name = base.Name;
			stateHandler.Start();
		}
		public void Stop()
		{
			if(stateHandler != null)
				stateHandler.Abort();
		}
		/// <summary>
		/// Ta metoda jest wywoływana z wątku pracującego w tle poprzez wywołanie
		/// BeginInvoke, dzięki czemu zawsze odbywa się szeregowanie do wątku
		/// zawierającego elementy sterujące listą.
		/// </summary>
		/// <param name="files"></param>
		/// <param name="startIndex"></param>
		/// <param name="count"></param>
		private void ColorChange(Color color)
		{
			this.BackColor = color;
			this.Refresh();
		}
		public event EventHandler PhilosopherLeave;
		/// <summary>
		/// Ta metoda jest wywoływana przez wątek pracujący w tle, 
		/// kiedy filozof zostanie poproszony o opuszczenie.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnPhilosopherLeave(object sender, EventArgs e)
		{
			if (PhilosopherLeave != null)
			{
				PhilosopherLeave(sender, e);
			}
		}
        
		private void InitializeComponent()
		{
		}
	
		private void StateHandler()
		{
			int startThinkingTime = 0;
			int stopThinkingTime = 0;
			int elapsed = 0;
			// Uzyskanie ciągu wyszukiwania. Przydziały poszczególnych pól
			// są w .NET niepodzielne, przez co nie ma potrzeby wykorzystania
			// synchronizacji wątku do przechwycenia wartości tego ciągu.
			try 
			{
				// Zmiana koloru na niebieski (rozmyślanie)
				BeginInvoke(colorChangeDelegate, new object[] {Color.Blue});
				startThinkingTime = Environment.TickCount;
				while(true)
				{
					if(WaitHandle.WaitAll(chopsticks, 100, false))
					{
						// Jedzenie
						BeginInvoke(colorChangeDelegate, new object[] {Color.Green});
						Thread.Sleep(500);
						chopsticks[0].ReleaseMutex();
						chopsticks[1].ReleaseMutex();
						BeginInvoke(colorChangeDelegate, new object[] {Color.Blue});
						startThinkingTime = Environment.TickCount;
						Thread.Sleep(0);
					}
					else
					{
						stopThinkingTime = Environment.TickCount;
						elapsed = stopThinkingTime - startThinkingTime;
						if(elapsed > 0 && elapsed <= 100)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.BlueViolet});
						}
						else if(elapsed > 100 && elapsed <= 200)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Violet});
						}
						else if(elapsed > 200 && elapsed <= 300)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.MediumVioletRed});
						}
						else if(elapsed > 300 && elapsed <= 400)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.PaleVioletRed});
						}
						else if(elapsed > 400 && elapsed <= 500)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Yellow});
						}
						else if(elapsed > 500 && elapsed <= 600)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Gold});
						}
						else if(elapsed > 600 && elapsed <= 700)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Orange});
						}
						else if(elapsed > 700 && elapsed <= 800)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.DarkOrange});
						}
						else if(elapsed > 800 && elapsed <= 900)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Chocolate});
						}
						else if(elapsed > 900 && elapsed <= 1000)
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.OrangeRed});
						}
						else
						{
							BeginInvoke(colorChangeDelegate, new object[] {Color.Red});
						}
					}
				}
			}
			finally
			{
				// Zakończono jedzenie i rozmyślanie

				// Zgłoszenie zdarzenia powiadamiającego użytkownika
				// o opuszczeniu stołu przez filozofa.  
				// Nie jest konieczne wykonanie tego poprzez szeregowane wywołanie,
				// ale szeregowanie jest zalecane z poniższej przyczyny.
				// Użytkownicy tego elementu sterującego nie wiedzą, że jest on wielowątkowy,
				// przez co mogą oczekiwać na powrót zdarzeń do tego samego wątku
				// co element sterujący.
				BeginInvoke(colorChangeDelegate, new object[] {Color.White});
				BeginInvoke(onPhilosopherLeave, new object[] {this, EventArgs.Empty});
			}
		}
	}

	/// <summary>
	/// Podsumowanie dla Form1.
	/// </summary>
	public class TableForm : System.Windows.Forms.Form
	{
		private const int numberOfPhilosophers = 20;
		private Mutex [] chopsticks;
		private System.Windows.Forms.Button philosopher1;
		private System.Windows.Forms.Button philosopher2;
		private System.Windows.Forms.Button philosopher3;
		private System.Windows.Forms.Button philosopher4;
		private System.Windows.Forms.Button philosopher5;
		private System.Windows.Forms.Button philosopher6;
		private System.Windows.Forms.Button philosopher7;
		private System.Windows.Forms.Button philosopher8;
		private System.Windows.Forms.Button philosopher9;
		private System.Windows.Forms.Button philosopher10;
		private System.Windows.Forms.Button philosopher11;
		private System.Windows.Forms.Button philosopher12;
		private System.Windows.Forms.Button philosopher13;
		private System.Windows.Forms.Button philosopher14;
		private System.Windows.Forms.Button philosopher15;
		private System.Windows.Forms.Button philosopher16;
		private System.Windows.Forms.Button philosopher17;
		private System.Windows.Forms.Button philosopher18;
		private System.Windows.Forms.Button philosopher19;
		private System.Windows.Forms.Button philosopher20;
		private System.Windows.Forms.Button startSession;
		private System.Windows.Forms.Button stopSession;
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public TableForm()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();
			foreach(Control b in Controls)
			{
				if(b is DiningPhilosopher)
				{
					b.BackColor = Color.White;
					b.Refresh();
				}
			}
		}

		/// <summary>
		/// Oczyszczenie używanych zasobów.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Metoda wymagana do obsługi Designera - nie należy modyfikować
		/// zawartości tej metody przy użyciu edytora kodu.
		/// </summary>
		private void InitializeComponent()
		{
			chopsticks = new Mutex[numberOfPhilosophers];
			for(int i = 0; i < numberOfPhilosophers; i++)
			{
				chopsticks[i] = new Mutex();
			}
			this.philosopher1 = new DiningPhilosopher(chopsticks[0], chopsticks[1]);
			this.philosopher2 = new DiningPhilosopher(chopsticks[1], chopsticks[2]);
			this.philosopher3 = new DiningPhilosopher(chopsticks[2], chopsticks[3]);
			this.philosopher4 = new DiningPhilosopher(chopsticks[3], chopsticks[4]);
			this.philosopher5 = new DiningPhilosopher(chopsticks[4], chopsticks[5]);
			this.philosopher6 = new DiningPhilosopher(chopsticks[5], chopsticks[6]);
			this.philosopher7 = new DiningPhilosopher(chopsticks[6], chopsticks[7]);
			this.philosopher8 = new DiningPhilosopher(chopsticks[7], chopsticks[8]);
			this.philosopher9 = new DiningPhilosopher(chopsticks[8], chopsticks[9]);
			this.philosopher10 = new DiningPhilosopher(chopsticks[9], chopsticks[10]);
			this.philosopher11 = new DiningPhilosopher(chopsticks[10], chopsticks[11]);
			this.philosopher12 = new DiningPhilosopher(chopsticks[11], chopsticks[12]);
			this.philosopher13 = new DiningPhilosopher(chopsticks[12], chopsticks[13]);
			this.philosopher14 = new DiningPhilosopher(chopsticks[13], chopsticks[14]);
			this.philosopher15 = new DiningPhilosopher(chopsticks[14], chopsticks[15]);
			this.philosopher16 = new DiningPhilosopher(chopsticks[15], chopsticks[16]);
			this.philosopher17 = new DiningPhilosopher(chopsticks[16], chopsticks[17]);
			this.philosopher18 = new DiningPhilosopher(chopsticks[17], chopsticks[18]);
			this.philosopher19 = new DiningPhilosopher(chopsticks[18], chopsticks[19]);
			this.philosopher20 = new DiningPhilosopher(chopsticks[19], chopsticks[0]);
			this.startSession = new System.Windows.Forms.Button();
			this.stopSession = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// philosopher1
			// 
			this.philosopher1.BackColor = System.Drawing.Color.Green;
			this.philosopher1.Location = new System.Drawing.Point(32, 128);
			this.philosopher1.Name = "philosopher1";
			this.philosopher1.Size = new System.Drawing.Size(24, 24);
			this.philosopher1.TabIndex = 0;
			// 
			// philosopher2
			// 
			this.philosopher2.BackColor = System.Drawing.Color.Green;
			this.philosopher2.Location = new System.Drawing.Point(32, 96);
			this.philosopher2.Name = "philosopher2";
			this.philosopher2.Size = new System.Drawing.Size(24, 24);
			this.philosopher2.TabIndex = 0;
			// 
			// philosopher3
			// 
			this.philosopher3.BackColor = System.Drawing.Color.Green;
			this.philosopher3.Location = new System.Drawing.Point(48, 64);
			this.philosopher3.Name = "philosopher3";
			this.philosopher3.Size = new System.Drawing.Size(24, 24);
			this.philosopher3.TabIndex = 0;
			// 
			// philosopher4
			// 
			this.philosopher4.BackColor = System.Drawing.Color.Green;
			this.philosopher4.Location = new System.Drawing.Point(88, 64);
			this.philosopher4.Name = "philosopher4";
			this.philosopher4.Size = new System.Drawing.Size(24, 24);
			this.philosopher4.TabIndex = 0;
			// 
			// philosopher5
			// 
			this.philosopher5.BackColor = System.Drawing.Color.Green;
			this.philosopher5.Location = new System.Drawing.Point(128, 64);
			this.philosopher5.Name = "philosopher5";
			this.philosopher5.Size = new System.Drawing.Size(24, 24);
			this.philosopher5.TabIndex = 0;
			// 
			// philosopher6
			// 
			this.philosopher6.BackColor = System.Drawing.Color.Green;
			this.philosopher6.Location = new System.Drawing.Point(168, 64);
			this.philosopher6.Name = "philosopher6";
			this.philosopher6.Size = new System.Drawing.Size(24, 24);
			this.philosopher6.TabIndex = 0;
			// 
			// philosopher7
			// 
			this.philosopher7.BackColor = System.Drawing.Color.Green;
			this.philosopher7.Location = new System.Drawing.Point(208, 64);
			this.philosopher7.Name = "philosopher7";
			this.philosopher7.Size = new System.Drawing.Size(24, 24);
			this.philosopher7.TabIndex = 0;
			// 
			// philosopher8
			// 
			this.philosopher8.BackColor = System.Drawing.Color.Green;
			this.philosopher8.Location = new System.Drawing.Point(248, 64);
			this.philosopher8.Name = "philosopher8";
			this.philosopher8.Size = new System.Drawing.Size(24, 24);
			this.philosopher8.TabIndex = 0;
			// 
			// philosopher9
			// 
			this.philosopher9.BackColor = System.Drawing.Color.Green;
			this.philosopher9.Location = new System.Drawing.Point(288, 64);
			this.philosopher9.Name = "philosopher9";
			this.philosopher9.Size = new System.Drawing.Size(24, 24);
			this.philosopher9.TabIndex = 0;
			// 
			// philosopher10
			// 
			this.philosopher10.BackColor = System.Drawing.Color.Green;
			this.philosopher10.Location = new System.Drawing.Point(304, 96);
			this.philosopher10.Name = "philosopher10";
			this.philosopher10.Size = new System.Drawing.Size(24, 24);
			this.philosopher10.TabIndex = 0;
			// 
			// philosopher11
			// 
			this.philosopher11.BackColor = System.Drawing.Color.Green;
			this.philosopher11.Location = new System.Drawing.Point(304, 128);
			this.philosopher11.Name = "philosopher11";
			this.philosopher11.Size = new System.Drawing.Size(24, 24);
			this.philosopher11.TabIndex = 0;
			// 
			// philosopher12
			// 
			this.philosopher12.BackColor = System.Drawing.Color.Green;
			this.philosopher12.Location = new System.Drawing.Point(304, 160);
			this.philosopher12.Name = "philosopher12";
			this.philosopher12.Size = new System.Drawing.Size(24, 24);
			this.philosopher12.TabIndex = 0;
			// 
			// philosopher13
			// 
			this.philosopher13.BackColor = System.Drawing.Color.Green;
			this.philosopher13.Location = new System.Drawing.Point(288, 192);
			this.philosopher13.Name = "philosopher13";
			this.philosopher13.Size = new System.Drawing.Size(24, 24);
			this.philosopher13.TabIndex = 0;
			// 
			// philosopher14
			// 
			this.philosopher14.BackColor = System.Drawing.Color.Green;
			this.philosopher14.Location = new System.Drawing.Point(248, 192);
			this.philosopher14.Name = "philosopher14";
			this.philosopher14.Size = new System.Drawing.Size(24, 24);
			this.philosopher14.TabIndex = 0;
			// 
			// philosopher15
			// 
			this.philosopher15.BackColor = System.Drawing.Color.Green;
			this.philosopher15.Location = new System.Drawing.Point(208, 192);
			this.philosopher15.Name = "philosopher15";
			this.philosopher15.Size = new System.Drawing.Size(24, 24);
			this.philosopher15.TabIndex = 0;
			// 
			// philosopher16
			// 
			this.philosopher16.BackColor = System.Drawing.Color.Green;
			this.philosopher16.Location = new System.Drawing.Point(168, 192);
			this.philosopher16.Name = "philosopher16";
			this.philosopher16.Size = new System.Drawing.Size(24, 24);
			this.philosopher16.TabIndex = 0;
			// 
			// philosopher17
			// 
			this.philosopher17.BackColor = System.Drawing.Color.Green;
			this.philosopher17.Location = new System.Drawing.Point(128, 192);
			this.philosopher17.Name = "philosopher17";
			this.philosopher17.Size = new System.Drawing.Size(24, 24);
			this.philosopher17.TabIndex = 0;
			// 
			// philosopher18
			// 
			this.philosopher18.BackColor = System.Drawing.Color.Green;
			this.philosopher18.Location = new System.Drawing.Point(88, 192);
			this.philosopher18.Name = "philosopher18";
			this.philosopher18.Size = new System.Drawing.Size(24, 24);
			this.philosopher18.TabIndex = 0;
			// 
			// philosopher19
			// 
			this.philosopher19.BackColor = System.Drawing.Color.Green;
			this.philosopher19.Location = new System.Drawing.Point(48, 192);
			this.philosopher19.Name = "philosopher19";
			this.philosopher19.Size = new System.Drawing.Size(24, 24);
			this.philosopher19.TabIndex = 0;
			// 
			// philosopher20
			// 
			this.philosopher20.BackColor = System.Drawing.Color.Green;
			this.philosopher20.Location = new System.Drawing.Point(32, 160);
			this.philosopher20.Name = "philosopher20";
			this.philosopher20.Size = new System.Drawing.Size(24, 24);
			this.philosopher20.TabIndex = 0;
			// 
			// startSession
			// 
			this.startSession.Location = new System.Drawing.Point(40, 240);
			this.startSession.Name = "startSession";
			this.startSession.TabIndex = 1;
			this.startSession.Text = "Start";
			this.startSession.Click += new System.EventHandler(this.OnStart);
			// 
			// stopSession
			// 
			this.stopSession.Location = new System.Drawing.Point(248, 240);
			this.stopSession.Name = "stopSession";
			this.stopSession.TabIndex = 2;
			this.stopSession.Text = "Stop";
			this.stopSession.Click += new System.EventHandler(this.OnStop);
			// 
			// TableForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(376, 273);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.stopSession,
																		  this.startSession,
																		  this.philosopher1,
																		  this.philosopher2,
																		  this.philosopher3,
																		  this.philosopher4,
																		  this.philosopher5,
																		  this.philosopher6,
																		  this.philosopher7,
																		  this.philosopher8,
																		  this.philosopher9,
																		  this.philosopher10,
																		  this.philosopher11,
																		  this.philosopher12,
																		  this.philosopher13,
																		  this.philosopher14,
																		  this.philosopher15,
																		  this.philosopher16,
																		  this.philosopher17,
																		  this.philosopher18,
																		  this.philosopher19,
																		  this.philosopher20});
			this.Name = "TableForm";
			this.Text = "Filozofowie";
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// Główny punkt wejścia dla aplikacji.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new TableForm());
		}

		private void OnStart(object sender, System.EventArgs e)
		{
			foreach(Control b in Controls)
			{
				if(b is DiningPhilosopher)
				{
					((DiningPhilosopher)b).Start();
				}
			}
		}

		private void OnStop(object sender, System.EventArgs e)
		{
			foreach(Control b in Controls)
			{
				if(b is DiningPhilosopher)
				{
					((DiningPhilosopher)b).Stop();
				}
			}
		}
	}
}
