// System.Runtime.Remoting.Channels.Simple.SimpleMessageFormat.cs
//
// Author:
//	DietmarMaurer (dietmar@ximian.com)
//
// (C) 2002 Ximian, Inc.  http://www.ximian.com

using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Runtime.Remoting.Messaging;

namespace System.Runtime.Remoting.Channels.Simple {

	public sealed class SimpleMessageFormat 
	{
		public enum MessageType : byte {
			Request = 0,
			Response = 1,
			Exception = 2,
			Unknown = 3
		}
		
		public SimpleMessageFormat ()
		{
		}
		
		public static void SendExceptionMessage (Stream network_stream, string message)
		{
			// we use the uri field to encode the message text
			SendMessageStream (network_stream, null, MessageType.Exception, message);
		}
		
		public static void SendMessageStream (Stream network_stream, MemoryStream data, 
						      MessageType msg_type, string uri)
		{
			int data_len = 0;
			
			if (data != null)
				data_len = (int)data.Length;

			int uri_len = 0;

			if (uri != null)
				uri_len = uri.Length;

			int header_len = 12 + uri_len * 2;
			int msg_len = data_len + header_len;
			
			byte [] buffer = new byte [msg_len];

			// magic header signature
			buffer [0] = 255;
			buffer [1] = 0;
			buffer [2] = 255;
			buffer [3] = (byte) msg_type;

			// data length
			buffer [4] = (byte) data_len;
			buffer [5] = (byte) (data_len >> 8);
			buffer [6] = (byte) (data_len >> 16);
			buffer [7] = (byte) (data_len >> 24);

			// uri length
			buffer [8] = (byte) uri_len;
			buffer [9] = (byte) (uri_len >> 8);
			buffer [10] = (byte) (uri_len >> 16);
			buffer [11] = (byte) (uri_len >> 24);

			// uri
			for (int i = 0; i < uri_len; i++) {
				buffer [12 + i*2] = (byte) uri [i];
				buffer [13 + i*2] = (byte) (uri [i] >> 8);
			}

			if (data_len > 0) {
				byte [] data_buffer = data.GetBuffer ();
				for (int i = 0; i < data_len; i++)
					buffer [i + header_len] = data_buffer [i];
			}
			
			network_stream.Write (buffer, 0, msg_len);
		}
		
		public static MemoryStream ReceiveMessageStream (Stream network_stream,
								 out MessageType msg_type,
								 out string uri)
		{
			int data_len = 0;
			int uri_len = 0;
			msg_type = MessageType.Unknown;
			uri = null;
			
			// search for message header (255, 0, 255, msg_type, msg_len)
			while (true) {
				while (true) {
					int x = network_stream.ReadByte ();
					if (x != 255)
						continue;
					x = network_stream.ReadByte ();
					if (x != 0)
						continue;
					x = network_stream.ReadByte ();
					if (x != 255)
						continue;
					break;
				}

				msg_type = (MessageType)network_stream.ReadByte ();
				
				byte [] buffer = new byte [8];
				
				int bytes_read = network_stream.Read (buffer, 0, 8);
				if (bytes_read != 8)
					continue;
				
				data_len = (buffer [0] | (buffer [1] << 8) |
					    (buffer [2] << 16) | (buffer [3] << 24));
				
				uri_len = (buffer [4] | (buffer [5] << 8) |
					   (buffer [6] << 16) | (buffer [7] << 24));
				
				if (uri_len > 0) {
					byte [] uri_buffer = new byte [uri_len * 2];
					bytes_read = network_stream.Read (uri_buffer, 0, uri_len * 2);
					if (bytes_read != (uri_len * 2))
						continue;
					char [] uri_array = new char [uri_len];
					for (int i = 0; i < uri_len; i++) {
						uri_array [i] = (char) (uri_buffer [i * 2] | (uri_buffer [(i * 2) + 1] << 8));
					}
					uri = new string (uri_array);
				}
				break;
			}

			if (msg_type == MessageType.Exception)
				throw new RemotingException ("\n" + uri + "\n" + "Rethrown at:\n");
			
			byte [] stream_buffer = new byte [data_len];
			if ((network_stream.Read (stream_buffer, 0, data_len)) != data_len)
				throw new Exception ("packet size error");
			
			return new MemoryStream (stream_buffer, false);
		}		
	}
}
