//
// System.Runtime.Remoting.Channels.CORBA.CORBAServerChannel.cs
//
// Author: Dietmar Maurer (dietmar@ximian.com)
//
// 2002 (C) Copyright, Ximian, Inc.
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace System.Runtime.Remoting.Channels.CORBA
{
	public class CORBAServerChannel : IChannelReceiver, IChannel
	{
		int port = 0;
		string name = "simple";
		string host;
		int priority = 1;
		Thread server_thread = null;
		TcpListener listener;
		CORBAServerTransportSink sink;
		ChannelDataStore channel_data;
		
		void Init (IServerChannelSinkProvider provider) {
			if (provider == null) {
				provider = new CORBAServerFormatterSinkProvider ();
			}
			
			IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (provider, this);

			host = Dns.GetHostByName(Dns.GetHostName()).HostName;
			
			string [] uris = null;
			
			if (port != 0) {
				uris = new String [1];
				uris [0] = GetChannelUri ();
			}
			
			channel_data = new ChannelDataStore (uris);;

			sink = new CORBAServerTransportSink (next_sink);
			
			listener = new TcpListener (port);
			StartListening (null);
		}
		
		public CORBAServerChannel (int port)
		{
			this.port = port;
			Init (null);
		}

		public CORBAServerChannel (IDictionary properties,
					 IServerChannelSinkProvider serverSinkProvider)
		{
			port = (int)properties ["port"];
			Init (serverSinkProvider);
		}

		public CORBAServerChannel (string name, int port,
					    IServerChannelSinkProvider serverSinkProvider)
		{
			name = name;
			this.port = port;
			Init (serverSinkProvider);
		}
		
		public CORBAServerChannel (string name, int port)
		{
			name = name;
			this.port = port;
			Init (null);
		}
		
		public object ChannelData
		{
			get {
				return channel_data;
			}
		}

		public string ChannelName
		{
			get {
				return name;
			}
		}

		public int ChannelPriority
		{
			get {
				return priority;
			}
		}

		string GetChannelUri ()
		{
			return "corba://" + host + ":" + port;
		}
		
		public string[] GetUrlsForUri (string uri)
		{
			string [] chnl_uris = channel_data.ChannelUris;
			
			if (uri.IndexOf ('/') != 0)
				uri = "/" + uri;

			string [] result = new String [chnl_uris.Length];

			for (int i = 0; i < chnl_uris.Length; i++) {
				result [i] = chnl_uris [i] + uri;
			}
			
			return result;
		}

		public string Parse (string url, out string objectURI)
		{
			int port;
			
			string host = CORBAChannel.ParseCORBAURL (url, out objectURI, out port);

			return GetChannelUri ();
		}

		void WaitForConnections ()
		{
			while (true) {
				TcpClient client = listener.AcceptTcpClient ();

				sink.InternalProcessMessage (client.GetStream ());

				client.Close ();
			}
		}
		
		public void StartListening (object data)
		{
			if (server_thread == null) {
				listener.Start ();
				if (port == 0) {
					port = ((IPEndPoint)listener.LocalEndpoint).Port;
					channel_data.ChannelUris = new String [1];
					channel_data.ChannelUris [0] = GetChannelUri ();
				}

				server_thread = new Thread (new ThreadStart (WaitForConnections));
				server_thread.Start ();
			}
		}

		public void StopListening (object data)
		{
			if (server_thread != null) {
				server_thread.Abort ();
				server_thread = null;
				listener.Stop ();
			}
		}
	}
}
