//
// 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 RegisterWaitForSingleObject
{
	public class Fibonacci 
	{
		ManualResetEvent jobstart = new ManualResetEvent(false);
		private	long fibnumber;
		private long result;
		private long startTime;
		private long stopTime;
		private long timeout;
		private long fib(long x) 
		{
			return ((x<=1)?1:(fib(x-1)+fib(x-2)));
		}
		public Fibonacci(long fibnumber, long timeout)
		{
			this.timeout = timeout;
			this.fibnumber = fibnumber;
			jobstart = new ManualResetEvent(false);
			startTime = 0;
			stopTime = 0;
		}
		public void Start()
		{
			startTime = Environment.TickCount;
		}
		public void Stop()
		{
			stopTime = Environment.TickCount;
		}
		public ManualResetEvent Event
		{
			get
			{
				return jobstart;
			}
		}
		public long Timeout
		{
			get
			{
				return timeout;
			}
		}
		public long Elapsed
		{
			get
			{
				return stopTime - startTime;
			}
		}
		public void Compute()
		{
			Start();
			result = fib(fibnumber);
			Stop();
			jobstart.Set();
		}
		public void Result(Object o, bool timedout)
		{
			if(!timedout)
			{
				RegisterWaitForSingleObjectForm form = (RegisterWaitForSingleObjectForm)o;
				form.BeginInvoke(form.Results, new object[]{fibnumber, result, Elapsed});
				fibnumber++;
				jobstart.Reset();
				Thread t = new Thread(new ThreadStart(Compute));
				t.Start();
				ThreadPool.RegisterWaitForSingleObject(jobstart, new WaitOrTimerCallback(Result), o, timeout, true);
			}
		}
	}
	public class RegisterWaitForSingleObjectForm : System.Windows.Forms.Form
	{
		private System.Windows.Forms.ListView fibonacciList;
		private System.Windows.Forms.ColumnHeader index;
		private System.Windows.Forms.ColumnHeader fibonacciNumber;
		public delegate void FibonacciResultsDelegate(long number, long result, long elapsed);
		private FibonacciResultsDelegate fibonacciResultsDelegate;
		private System.Windows.Forms.ColumnHeader elapsedTime;
		private System.Windows.Forms.Button startButton;
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public RegisterWaitForSingleObjectForm()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();
		}
		public FibonacciResultsDelegate Results
		{
			get
			{
				return fibonacciResultsDelegate;
			}
		}

		private void OnFibonacciResults(long number, long result, long elapsed)
		{
			ListViewItem item = new ListViewItem(Convert.ToString(number));
			item.SubItems.Add(Convert.ToString(result));
			item.SubItems.Add(Convert.ToString(elapsed));
			fibonacciList.Items.Add(item);
		}

		/// <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()
		{
			this.startButton = new System.Windows.Forms.Button();
			this.fibonacciList = new System.Windows.Forms.ListView();
			this.index = new System.Windows.Forms.ColumnHeader();
			this.fibonacciNumber = new System.Windows.Forms.ColumnHeader();
			this.elapsedTime = new System.Windows.Forms.ColumnHeader();
			this.SuspendLayout();
			// 
			// startButton
			// 
			this.startButton.Location = new System.Drawing.Point(8, 272);
			this.startButton.Name = "startButton";
			this.startButton.TabIndex = 1;
			this.startButton.Text = "Start";
			this.startButton.Click += new System.EventHandler(this.OnStart);
			// 
			// fibonacciList
			// 
			this.fibonacciList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
																							this.index,
																							this.fibonacciNumber,
																							this.elapsedTime});
			this.fibonacciList.Location = new System.Drawing.Point(8, 8);
			this.fibonacciList.Name = "fibonacciList";
			this.fibonacciList.Size = new System.Drawing.Size(344, 248);
			this.fibonacciList.TabIndex = 0;
			this.fibonacciList.View = System.Windows.Forms.View.Details;
			// 
			// index
			// 
			this.index.Text = "Indeks";
			// 
			// fibonacciNumber
			// 
			this.fibonacciNumber.Text = "Liczba Fibonacciego";
			this.fibonacciNumber.Width = 180;
			// 
			// elapsedTime
			// 
			this.elapsedTime.Text = "Upłynęło";
			this.elapsedTime.Width = 99;
			// 
			// RegisterWaitForSingleObjectForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(360, 373);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.startButton,
																		  this.fibonacciList});
			this.Name = "RegisterWaitForSingleObjectForm";
			this.Text = "RegisterWaitForSingleObject Test";
			this.ResumeLayout(false);

		}
		#endregion

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

		private void OnStart(object sender, System.EventArgs e)
		{
			fibonacciList.Items.Clear();
			fibonacciResultsDelegate = new FibonacciResultsDelegate(OnFibonacciResults);
			Fibonacci fib = new Fibonacci(0, 5000);
			WaitOrTimerCallback	callback = new WaitOrTimerCallback(fib.Result);
			try 
			{
				fib.Event.Reset();
				Thread t = new Thread(new ThreadStart(fib.Compute));
				t.Start();
				ThreadPool.RegisterWaitForSingleObject(fib.Event, callback, this, fib.Timeout, true);
			}
			catch (NotSupportedException) 
			{
				Debug.WriteLine("Przechwycono wyjątek NotSupportedException, ponieważ: ");
				Debug.WriteLine("\tSystem.ThreadPool.RegisterWaitForSingleObject nie jest obsługiwany na tym systemie.");
				Debug.WriteLine("\tKonieczność uruchomienia w systemie Win2K lub użycia dodatkowej obsługi Win32");
			}
		}
	}
}
