//
// 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.Configuration;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;

namespace Messenger
{
	public enum ListType
	{
		FL = 0,
		RL,
		AL,
		BL
	};
	public enum StateType
	{
		NLN = 0,
		FLN,
		HDN
	};
	public enum SubStateType
	{
		NLN = 0,
		BSY,
		IDL,
		BRB,
		AWY,
		PHN,
		LUN
	};

	public delegate void Status(StatusMessageType type, string message);
	public delegate void List(ListType type, int index, int count, string user, string alias);
	public delegate void Ring(int tid, IPEndPoint ep, string code, string user, string alias);
	public delegate void Hangup(string user);
	public delegate void Change(StateType state);
	public delegate void ClientOffline(string user);
	public delegate void ClientInitial(int tid, SubStateType substate, string user, string alias);
	public delegate void ClientOnline(SubStateType substate, string user, string alias);

	/// <summary>
	/// Podsumowanie dla MessengerForm.
	/// </summary>
	public class MessengerForm : System.Windows.Forms.Form
	{
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		
		private static int _id = 1;

		private StatusForm statusForm = null;
		private NS _ns = null;
		private Hashtable conversations = null;

		private string _MSNHostServer;
		private string _MSNPassportEmail;
		private string _MSNPassportPassword;
		private string _MSNNickname;
		private System.Windows.Forms.ComboBox forwardList;
		private System.Windows.Forms.ComboBox reverseList;
		private System.Windows.Forms.ComboBox allowList;
		private System.Windows.Forms.ComboBox blockList;
		private System.Windows.Forms.Label label1;
		private System.Windows.Forms.Label label2;
		private System.Windows.Forms.Label label3;
		private System.Windows.Forms.Label label4;
		private System.Windows.Forms.ListView clientList;

		private System.ComponentModel.Container components = null;

		private void StatusCallback(StatusMessageType type, string message)
		{
			if(statusForm == null)
				return;
			statusForm.StatusCallback(type, message);
		}

		private void ListCallback(ListType type, int index, int count, string user, string alias)
		{
			switch(type)
			{
				case ListType.FL:
					forwardList.Items.Add(user);
					if(forwardList.Items.Count == 1)
					{
						forwardList.Text = user;
					}
					break;
				case ListType.RL:
					reverseList.Items.Add(user);
					if(reverseList.Items.Count == 1)
					{
						reverseList.Text = user;
					}
					break;
				case ListType.AL:
					allowList.Items.Add(user);
					if(allowList.Items.Count == 1)
					{
						allowList.Text = user;
					}
					break;
				case ListType.BL:
					blockList.Items.Add(user);
					if(blockList.Items.Count == 1)
					{
						blockList.Text = user;
					}
					break;
			}
		}

		private void ChangeCallback(StateType state)
		{
			string title = "Mesenger Form - ";
			switch(state)
			{
				case StateType.NLN:
					title += "Online";
					break;
				case StateType.FLN:
					title += "Offline";
					break;
				case StateType.HDN:
					title += "Ukryty";
					break;
				default:
					title += "Nieznany";
					break;
			}
			Text = title;
		}
		private void OfflineCallback(string user)
		{
			ListViewItem initialItem = null;
			foreach(ListViewItem item in clientList.Items)
			{
				if(item.Text == user)
					initialItem = item;
			}
			if(initialItem == null)
				initialItem = clientList.Items.Add(user);
			initialItem.SubItems.Add("Offline");
		}
		private void InitialCallback(int tid, SubStateType substate, string user, string alias)
		{
			ListViewItem initialItem = null;
			foreach(ListViewItem item in clientList.Items)
			{
				if(item.Text == user)
					initialItem = item;
			}
			string t = null;
			switch(substate)
			{
				case SubStateType.NLN:
					t = "Online";
					break;
				case SubStateType.BSY:
					t = "Zajęty";
					break;
				case SubStateType.IDL:
					t = "Wolny";
					break;
				case SubStateType.BRB:
					t = "Zaraz wracam";
					break;
				case SubStateType.AWY:
					t = "Nie ma mnie";
					break;
				case SubStateType.PHN:
					t = "Rozmawiam";
					break;
				case SubStateType.LUN:
					t = "Poszedłem jeść";
					break;
			}
			if(initialItem == null)
				initialItem = clientList.Items.Add(user);
			initialItem.SubItems.Add(t);
		}
		private void OnlineCallback(SubStateType substate, string user, string alias)
		{
			ListViewItem initialItem = null;
			foreach(ListViewItem item in clientList.Items)
			{
				if(item.Text == user)
					initialItem = item;
			}
			string t = null;
			switch(substate)
			{
				case SubStateType.NLN:
					t = "Online";
					break;
				case SubStateType.BSY:
					t = "Zajęty";
					break;
				case SubStateType.IDL:
					t = "Wolny";
					break;
				case SubStateType.BRB:
					t = "Zaraz wracam";
					break;
				case SubStateType.AWY:
					t = "Nie ma mnie";
					break;
				case SubStateType.PHN:
					t = "Rozmawiam";
					break;
				case SubStateType.LUN:
					t = "Poszedłem jeść";
					break;
			}
			if(initialItem == null)
				initialItem = clientList.Items.Add(user);
			initialItem.SubItems.Add(t);
		}

		private static string ReceiveSocketData(Socket s)
		{
			string strData = "";
			byte[] buff;
			int timeout = 300;
			int timepos = 0;

			while (s.Available == 0) 
			{
				if (timepos >= timeout)
				{
					break;
				}
				timepos++;
				Thread.Sleep(50);
			}

			if (s.Available > 0)
			{
				buff = new byte[s.Available];
				s.Receive(buff, s.Available, SocketFlags.None);
				strData = UTF8Encoding.UTF8.GetString(buff);
			}
			return strData;
		}

		private void HangupCallback(string user)
		{
			Debug.WriteLine("Rozłączanie " + user);
			object o = conversations[user];
			ChatForm f = o as ChatForm;
			if(f != null)
			{
				Debug.WriteLine("Zamykanie " + user);
				f.Close();
			}
			conversations.Remove(user);
		}

		private void RingCallback(int session, IPEndPoint ep, string code, string user, string alias)
		{
			if(conversations.ContainsKey(user))
			{
				return;
			}
			Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
			s.Connect(ep);

			string tid = Convert.ToString(GetNextTrialID());

			string t = "ANS " + tid + " " + _MSNPassportEmail + " " + code + " " + Convert.ToString(session);
			StatusCallback(StatusMessageType.Request, t);
			s.Send(Encoding.UTF8.GetBytes(t + "\n"));

			string response = MessengerForm.ReceiveSocketData(s);
			StatusCallback(StatusMessageType.Response, response);
			Regex iroregex = new Regex(@"(?<IRO>IRO) (?<tid>[0-9]+) (?<pid>[0-9]+) (?<participants>[0-9]+) (?<user>[^ ]+) (?<custom>.+)");
			Match mc =iroregex.Match(response);
			if ( !mc.Success )
			{
				return;
			}

			ChatForm cf = new ChatForm();

			cf.Client = _MSNPassportEmail;
			cf.User = mc.Groups["user"].Value;
			cf.Nickname = mc.Groups["custom"].Value;
			cf.Connection = s;
			cf.Root = this;
			cf.HangupCallback += new Hangup(HangupCallback);
			cf.Start();

			conversations.Add(user, cf);
			cf.Show();
		}

		static public int GetNextTrialID()
		{
			return _id++;
		}
			
		public MessengerForm()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();

			clientList.Columns.Add("User",200,HorizontalAlignment.Center );
			clientList.Columns.Add("Status",100,HorizontalAlignment.Center );

			// Status
			statusForm = new StatusForm();
			statusForm.Show();
			// Odczyt konfiguracji

			StatusCallback(StatusMessageType.Status, "Reading Configuration...");

			AppSettingsReader asr = new AppSettingsReader();
			_MSNHostServer = (string)asr.GetValue("MSNHostServer", typeof(string));
			_MSNPassportEmail = (string)asr.GetValue("MSNPassportEmail", typeof(string));
			_MSNPassportPassword = (string)asr.GetValue("MSNPassportPassword", typeof(string));
			_MSNNickname = (string)asr.GetValue("MSNNickname", typeof(string));

			StatusCallback(StatusMessageType.Status, _MSNHostServer);
			StatusCallback(StatusMessageType.Status, _MSNPassportEmail);
			StatusCallback(StatusMessageType.Status, _MSNNickname);

			_ns = new NS();
			_ns.Host = _MSNHostServer;
			_ns.Email = _MSNPassportEmail;
			_ns.Password = _MSNPassportPassword;
			_ns.Nickname = _MSNNickname;
			_ns.ParentForm = this;
	
			conversations = new Hashtable();

			_ns.StatusUpdate += new Status(StatusCallback);
			_ns.ListUpdate += new List(ListCallback);
			_ns.RingCallback += new Ring(RingCallback);
			_ns.ChangeCallback += new Change(ChangeCallback);
			_ns.OfflineCallback += new ClientOffline(OfflineCallback);
			_ns.InitialCallback += new ClientInitial(InitialCallback);
			_ns.OnlineCallback += new ClientOnline(OnlineCallback);

			_ns.Connect(_MSNHostServer);
			// Uwierzytelnianie na serwerze NS Server
			try
			{
				Console.WriteLine("Uwierzytelnianie na serwerze NS Server...");
				_ns.Authenticate(_MSNPassportEmail, _MSNPassportPassword);
				_ns.Start();
			}
			catch(ApplicationException e)
			{
				StatusCallback(StatusMessageType.Status, e.ToString());
			}
		}

		/// <summary>
		/// Oczyszczenie używanych zasobów.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
				foreach(object o in conversations)
				{
					ChatForm f = o as ChatForm;
					if(f != null)
					{
						f.Close();
					}
				}
				conversations.Clear();
				if(_ns != null)
				{
					_ns.Close();
				}
			}
			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.forwardList = new System.Windows.Forms.ComboBox();
			this.reverseList = new System.Windows.Forms.ComboBox();
			this.allowList = new System.Windows.Forms.ComboBox();
			this.blockList = new System.Windows.Forms.ComboBox();
			this.label1 = new System.Windows.Forms.Label();
			this.label2 = new System.Windows.Forms.Label();
			this.label3 = new System.Windows.Forms.Label();
			this.label4 = new System.Windows.Forms.Label();
			this.clientList = new System.Windows.Forms.ListView();
			this.SuspendLayout();
			// 
			// forwardList
			// 
			this.forwardList.Location = new System.Drawing.Point(10, 39);
			this.forwardList.Name = "forwardList";
			this.forwardList.Size = new System.Drawing.Size(215, 24);
			this.forwardList.TabIndex = 0;
			// 
			// reverseList
			// 
			this.reverseList.Location = new System.Drawing.Point(10, 99);
			this.reverseList.Name = "reverseList";
			this.reverseList.Size = new System.Drawing.Size(215, 24);
			this.reverseList.TabIndex = 1;
			// 
			// allowList
			// 
			this.allowList.Location = new System.Drawing.Point(10, 158);
			this.allowList.Name = "allowList";
			this.allowList.Size = new System.Drawing.Size(215, 24);
			this.allowList.TabIndex = 2;
			// 
			// blockList
			// 
			this.blockList.Location = new System.Drawing.Point(10, 217);
			this.blockList.Name = "blockList";
			this.blockList.Size = new System.Drawing.Size(215, 24);
			this.blockList.TabIndex = 3;
			// 
			// label1
			// 
			this.label1.Location = new System.Drawing.Point(10, 10);
			this.label1.Name = "label1";
			this.label1.Size = new System.Drawing.Size(215, 28);
			this.label1.TabIndex = 4;
			this.label1.Text = "W górę listy";
			// 
			// label2
			// 
			this.label2.Location = new System.Drawing.Point(10, 69);
			this.label2.Name = "label2";
			this.label2.Size = new System.Drawing.Size(215, 28);
			this.label2.TabIndex = 5;
			this.label2.Text = "W dół listy";
			// 
			// label3
			// 
			this.label3.Location = new System.Drawing.Point(10, 128);
			this.label3.Name = "label3";
			this.label3.Size = new System.Drawing.Size(215, 29);
			this.label3.TabIndex = 6;
			this.label3.Text = "Lista gości";
			// 
			// label4
			// 
			this.label4.Location = new System.Drawing.Point(10, 188);
			this.label4.Name = "label4";
			this.label4.Size = new System.Drawing.Size(215, 28);
			this.label4.TabIndex = 7;
			this.label4.Text = "Lista zablokowanych";
			// 
			// clientList
			// 
			this.clientList.Location = new System.Drawing.Point(240, 16);
			this.clientList.Name = "clientList";
			this.clientList.Size = new System.Drawing.Size(360, 256);
			this.clientList.TabIndex = 8;
			this.clientList.View = System.Windows.Forms.View.Details;
			// 
			// MessengerForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
			this.ClientSize = new System.Drawing.Size(614, 283);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.clientList,
																		  this.label4,
																		  this.label3,
																		  this.label2,
																		  this.label1,
																		  this.blockList,
																		  this.allowList,
																		  this.reverseList,
																		  this.forwardList});
			this.Name = "MessengerForm";
			this.Text = "Formularz Messenger";
			this.ResumeLayout(false);

		}
		#endregion

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