//
// 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.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;

namespace Messenger
{
	/// <summary>
	/// Podsumowanie dla ChatForm.
	/// </summary>
	public class ChatForm : System.Windows.Forms.Form
	{
		public Hangup HangupCallback;
		private string client;
		private string user;
		private string nickname;
		private Socket socket;
		private MessengerForm parent;
		private Thread thread;
		private System.Windows.Forms.RichTextBox conversation;
		private System.Windows.Forms.StatusBar statusBar;
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		private System.ComponentModel.Container components = null;

		private void ChatSocketMain()
		{
			string strData;
			byte[] buff;
			while (socket != null && socket.Connected)
			{
				if (socket.Available > 0)
				{
					try
					{
						buff = new byte[socket.Available];
						socket.Receive(buff, socket.Available, SocketFlags.None);
						strData = UTF8Encoding.UTF8.GetString(buff);
						ProcessSBData(socket, strData);
					}
					catch (SocketException sec)
					{
						Debug.WriteLine(sec.Message + sec.StackTrace);
					}
					catch (ThreadAbortException)
					{
					}
					catch (Exception ec)
					{
						Debug.WriteLine(ec.Message + ec.StackTrace);
					}
				}			
			}
			if(socket.Connected == false)
			{
				Debug.WriteLine(user + " disconnected");
			}
		}

		private void ProcessSBData(Socket s, string strData)
		{
			//   Wysłanie błyskawicznej wiadomości
			//
			//   Po nawiązaniu sesji między klientami poprzez Switchboard Server
			//   wysyłanie wiadomości do uczestników sesji odbywa się
			//   w następujący sposób:
			//
			//        C: MSG TrID [U | N | A] Długość\r\nWiadomość
			//        S: NAK TrID
			//        S: ACK TrID
			//
			//   U, N i A to trzy tryby potwierdzania odbioru wiadomości:
			//   Unacknowledged (bez potwierdzania), Negative-Acknowledgement-Only 
			//   (tylko błędy) oraz Acknowledgement (potwierdzanie).
			//   W zależności od wartości tego parametru Switchboard Serve nie prześle nic
			//   lub prześle do klienta NAK albo ACK.
			//
			//   W trybie Unacknowledged serwer Switchboard Server nie informuje
			//   nadawcy o sukcesie lub niepowodzeniu w czasie doręczania wiadomości.
			//
			//   W trybie Negative-Acknowledgement-Only serwer Switchboard Server
			//   odpowiada klientowi tylko jeśli wiadomość nie została doręczona
			//   do adresata.
			//
			//   Tryb Acknowledgement nie został jeszcze zaimplementowany.
			//
			//   Długość to rozmiar parametru Wiadomość, podczas gdy Wiadomość to
			//   przesyłana wiadomość.

			Debug.WriteLine("Odebrano SB (" + user + ") Dane: \n" + strData);
			if (strData.IndexOf("ANS") != -1)
			{		
			}
			if (strData.IndexOf("MSG") != -1)
			{		
				Regex tuserregex = new Regex(@"(?<MSG>MSG) (?<user>[^ ]+) (?<alias>[^ ]+) (?<length>[0-9]+)\r\n(?<header>(.+\r\n)+TypingUser: (?<tuser>.+)\r\n\r\n)");
				Match mc = tuserregex.Match(strData);
				if(mc.Success)
				{
					statusBar.Text = "Wpisz użytkownika: " + mc.Groups["tuser"].Value;
				}
				else
				{
					Regex msgregex = new Regex(@"(?<MSG>MSG) (?<user>[^ ]+) (?<alias>[^ ]+) (?<length>[0-9]+)\r\n(?<header>(.+\r\n)+\r\n)");
					mc = msgregex.Match(strData);
					if(mc.Success)
					{
						string msg = strData.Substring(mc.Length);
						msg.TrimEnd();
						conversation.AppendText(msg + "\n");
						statusBar.Text = "";
					}
				}
			}
			if (strData.IndexOf("BYE") != -1)
			{		
				if(HangupCallback != null)
				{
					Regex byeregex = new Regex(@"(?<BYE>BYE) (?<user>.+)");
					Match mc = byeregex.Match(strData);
					if(mc.Success)
					{
						parent.BeginInvoke(HangupCallback,
							new object[] { 	mc.Groups["user"].Value.TrimEnd() } );
					}
				}
			}
		}

		public ChatForm()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();
		}

		public string Client
		{
			get
			{
				return client;
			}
			set
			{
				client = value;
			}
		}
		public string User
		{
			get
			{
				return user;
			}
			set
			{
				user = value;
			}
		}
		public string Nickname
		{
			get
			{
				return nickname;
			}
			set
			{
				nickname = value;
			}
		}
		public Socket Connection
		{
			get
			{
				return socket;
			}
			set
			{
				socket = value;
			}
		}
		public MessengerForm Root
		{
			get
			{
				return parent;
			}
			set
			{
				parent = value;
			}
		}
		public void Start()
		{
			thread = new Thread(new ThreadStart(ChatSocketMain));
			thread.Name = "SB " + user;
			thread.Start();
		}

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

				if(thread != null && thread.ThreadState != System.Threading.ThreadState.Unstarted)
				{
					thread.Abort();
					thread.Join();
				}

				socket.Close();
				socket = null;
			}
			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.conversation = new System.Windows.Forms.RichTextBox();
			this.statusBar = new System.Windows.Forms.StatusBar();
			this.SuspendLayout();
			// 
			// conversation
			// 
			this.conversation.Location = new System.Drawing.Point(9, 56);
			this.conversation.Name = "conversation";
			this.conversation.ReadOnly = true;
			this.conversation.Size = new System.Drawing.Size(497, 234);
			this.conversation.TabIndex = 1;
			this.conversation.Text = "";
			// 
			// statusBar
			// 
			this.statusBar.Location = new System.Drawing.Point(0, 292);
			this.statusBar.Name = "statusBar";
			this.statusBar.Size = new System.Drawing.Size(523, 25);
			this.statusBar.TabIndex = 2;
			// 
			// ChatForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
			this.ClientSize = new System.Drawing.Size(523, 317);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.statusBar,
																		  this.conversation});
			this.Name = "ChatForm";
			this.Text = "ChatForm";
			this.ResumeLayout(false);

		}
		#endregion

	}
}
