/* Transport Security Layer (TLS)
 * Copyright (c) 2003 Carlos Guzmán Álvarez
 * 
 * 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;
using System.IO;
using System.Net;

namespace Mono.Security.Protocol.Tls
{
	internal class TlsStream : Stream
	{
		#region FIELDS

		private bool				canRead;
		private bool				canWrite;
		private MemoryStream		buffer;

		#endregion

		#region PROPERTIES

		public bool EOF
		{
			get 
			{
				if (this.Position < this.Length)
				{
					return false;
				}
				else
				{
					return true;
				}
			}
		}

		#endregion

		#region STREAM_PROPERTIES

		public override bool CanWrite
		{
			get { return canWrite; }
		}

		public override bool CanRead
		{
			get { return canRead; }
		}

		public override bool CanSeek
		{
			get { return buffer.CanSeek; }
		}

		public override long Position
		{
			get { return buffer.Position; }
			set { buffer.Position = value; }
		}

		public override long Length
		{
			get { return buffer.Length; }
		}

		#endregion

		#region CONSTRUCTORS

		public TlsStream() : base()
		{
			this.buffer		= new MemoryStream(0);
			this.canRead	= false;
			this.canWrite	= true;
		}

		public TlsStream(byte[] data) : base()
		{
			this.buffer		= new MemoryStream(data);
			this.canRead	= true;
			this.canWrite	= false;
		}

		#endregion

		#region SPECIFIC_READ_METHODS

		public new byte ReadByte()
		{
			return (byte)base.ReadByte();
		}

		public short ReadInt16()
		{
			byte[] bytes = this.ReadBytes(2);

			return IPAddress.HostToNetworkOrder(BitConverter.ToInt16(bytes, 0));
		}

		public int ReadInt24()
		{
			byte[] b = this.ReadBytes(3);
			
			return ((b[0] & 0xff) << 16) | ((b[1] & 0xff) << 8) | (b[2] & 0xff);
		}

		public int ReadInt32()
		{
			byte[] bytes = this.ReadBytes(4);

			return IPAddress.HostToNetworkOrder(BitConverter.ToInt32(bytes, 0));
		}

		public long ReadInt64()
		{
			byte[] bytes = this.ReadBytes(8);
						
			return IPAddress.HostToNetworkOrder(BitConverter.ToInt64(bytes, 0));
		}

		public byte[] ReadBytes(int count)
		{
			byte[] bytes = new byte[count];
			this.Read(bytes, 0, count);

			return bytes;
		}

		#endregion

		#region SPECIFIC_WRITE_METHODS

		public void Write(byte value)
		{
			WriteByte(value);
		}

		public void Write(short value)
		{
			byte[] bytes = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder(value));
			Write(bytes);
		}

		public void WriteInt24(int value)
		{
			int int24 = IPAddress.HostToNetworkOrder(value);
			byte[] content = new byte[3];
				
			System.Array.Copy(BitConverter.GetBytes(int24), 1, content, 0, 3);

			Write(content);
		}

		public void Write(int value)
		{
			byte[] bytes = BitConverter.GetBytes((int)IPAddress.HostToNetworkOrder(value));
			Write(bytes);
		}

		public void Write(long value)
		{
			byte[] bytes = BitConverter.GetBytes((long)IPAddress.HostToNetworkOrder(value));
			Write(bytes);
		}

		public void Write(byte[] buffer)
		{
			Write(buffer, 0, buffer.Length);
		}

		#endregion

		#region METHODS

		public void Reset()
		{
			buffer.SetLength(0);
			buffer.Position = 0;
		}

		public byte[] ToArray()
		{
			return buffer.ToArray();
		}

		#endregion

		#region STREAM_METHODS

		public override void Flush()
		{
			buffer.Flush();
		}

		public override void SetLength(long length)
		{
			buffer.SetLength(length);
		}

		public override long Seek(long offset, System.IO.SeekOrigin loc)
		{
			return buffer.Seek(offset, loc);
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			if (canRead)
			{
				return this.buffer.Read(buffer, offset, count);
			}
			throw new InvalidOperationException("Read operations are not allowed by this stream");
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			if (canWrite)
			{
				this.buffer.Write(buffer, offset, count);
			}
			else
			{
				throw new InvalidOperationException("Write operations are not allowed by this stream");
			}
		}

		#endregion
	}
}
