using System;
using System.Collections.Specialized;
using System.Threading;

namespace I2P.SAM.Client
{
	/// <summary>
	///   Optional base class providing basic SAM event handling and helper
	///   methods.
	/// </summary>
	public class SamBaseEventHandler
	{
		private object              _helloLock         = new Object();
		private string              _helloOk;
		private NameValueCollection _namingReplies     = new NameValueCollection();
		private object              _namingReplyLock   = new Object();
		private SamReader           _samReader;
		private object              _sessionCreateLock = new Object();
		private string              _sessionCreateOk;

		/// <summary>
		///   Creates a new <c>SamBaseEventHandler</c> instance and registers
		///   overridable handler methods for all events generated by the given
		///   <see cref="SamReader">SamReader</see>.
		/// </summary>
		public SamBaseEventHandler(SamReader samReader) {
			_samReader = samReader;
			_samReader.DestReplyReceived       += new DestReplyReceivedHandler(OnDestReplyReceived);
			_samReader.HelloReplyReceived      += new HelloReplyReceivedHandler(OnHelloReplyReceived);
			_samReader.NamingReplyReceived     += new NamingReplyReceivedHandler(OnNamingReplyReceived);
			_samReader.SessionStatusReceived   += new SessionStatusReceivedHandler(OnSessionStatusReceived);
			_samReader.StreamClosedReceived    += new StreamClosedReceivedHandler(OnStreamClosedReceived);
			_samReader.StreamConnectedReceived += new StreamConnectedReceivedHandler(OnStreamConnectedReceived);
			_samReader.StreamDataReceived      += new StreamDataReceivedHandler(OnStreamDataReceived);
			_samReader.StreamStatusReceived    += new StreamStatusReceivedHandler(OnStreamStatusReceived);
			_samReader.UnknownMessageReceived  += new UnknownMessageReceivedHandler(OnUnknownMessageReceived);
		}

		public virtual void OnDestReplyReceived(string publicKey, string privateKey) {
		}

		public virtual void OnHelloReplyReceived(bool ok) {
			lock (_helloLock) {
				if (ok)
					_helloOk = Boolean.TrueString;
				else
					_helloOk = Boolean.FalseString;

				Monitor.PulseAll(_helloLock);
			}
		}

		public virtual void OnNamingReplyReceived(string name, string result, string valueString, string message) {
			lock (_namingReplyLock) {
				if (result.Equals(SamBridgeMessages.NAMING_REPLY_OK))
					_namingReplies.Add(name, valueString);
				else
					_namingReplies.Add(name, result);

				Monitor.PulseAll(_namingReplyLock);
			}
		}

		public virtual void OnSessionStatusReceived(string result, string destination, string message) {
			lock (_sessionCreateLock) {
				if (result.Equals(SamBridgeMessages.SESSION_STATUS_OK))
					_sessionCreateOk = Boolean.TrueString;
				else
					_sessionCreateOk = Boolean.FalseString;

				Monitor.PulseAll(_sessionCreateLock);
			}
		}

		public virtual void OnStreamClosedReceived(string result, int id, string message) {
		}

		public virtual void OnStreamConnectedReceived(string remoteDestination, int id) {
		}

		public virtual void OnStreamDataReceived(int id, byte[] data, int offset, int length) {
		}

		public virtual void OnStreamStatusReceived(string result, int id, string message) {
		}

	    public virtual void OnUnknownMessageReceived(string major, string minor, NameValueCollection parameters) {
			Console.WriteLine("wrt, [" + major + "] [" + minor + "] [" + parameters + "]");
	    }
	    
	// Helper methods below.

		/// <summary>
		///   Waits for a SAM connection to be established.
		/// </summary>
		/// <remarks>
		///   This method blocks until the connection is established.
		/// </remarks>
		/// <returns><c>true</c> if the handshake was successful.</returns>
		public virtual bool WaitForHelloReply() {
			while (true) {
				lock (_helloLock) {
					if (_helloOk == null)
						Monitor.Wait(_helloLock);
					else
						return Boolean.Parse(_helloOk);
				}
			}
		}

		/// <summary>
		///   Waits for a SAM naming reply message.
		/// </summary>
		/// <remarks>
		///   This method blocks until all naming replies are received.
		/// </remarks>
		/// <param name="name">The name to be looked for, or <c>ME</c>.</param>
		/// <returns>The matching destination for <c>name</c>, or <c>null</c> if
		///   the key was not able to be retrieved.</returns>
		public virtual string WaitForNamingReply(string name) {
			while (true) {
				lock (_namingReplyLock) {
					try {
						string valueString = _namingReplies[name];
						_namingReplies.Remove(name);

						if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_INVALID_KEY))
							return null;
						else if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_KEY_NOT_FOUND))
							return null;
						else
							return valueString;

					} catch (ArgumentNullException ane) {
						Monitor.Wait(_namingReplyLock);
					}
				}
			}
		}

		/// <summary>
		///   Waits for a SAM session to be created.
		/// </summary>
		/// <remarks>
		///   This method blocks until a SAM session is created.
		/// </remarks>
		/// <returns><c>true</c> if the SAM session was created successfully.
		//  </returns>
		public virtual bool WaitForSessionCreateReply() {
			while (true) {
				lock (_sessionCreateLock) {
					if (_sessionCreateOk == null)
						Monitor.Wait(_sessionCreateLock);
					else
						return Boolean.Parse(_sessionCreateOk);
				}
			}
		}
	}
}