/*
 * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
using System;
using System.IO;
namespace SafeExamBrowser.Configuration
{
	/// 
	/// A read-only wrapper for a subsection of another, larger stream.
	/// 
	internal class SubStream : Stream
	{
		private long length;
		private long offset;
		private Stream original;
		public override bool CanRead => original.CanRead;
		public override bool CanSeek => original.CanSeek;
		public override bool CanWrite => false;
		public override long Length => length;
		public override long Position { get; set; }
		/// 
		/// Creates a new wrapper for the specified subsection of the given stream.
		/// 
		/// 
		/// 
		/// Below an example of a subsection within a stream:
		/// 
		/// +==============+==============================================================+==============================+
		/// |      ...     |####################### subsection ###########################|              ...             |
		/// +==============+==============================================================+==============================+
		/// ^              ^                                                              ^                              ^
		/// |              |                                                              |                              |
		/// |              + offset                                                       + length                       |
		/// |                                                                                                            |
		/// + start of original                                                                          end of original +
		/// 
		/// 
		/// In case the original stream does not support .
		/// In case the original stream does not support .
		/// In case the specified subsection is outside the bounds of the original stream.
		public SubStream(Stream original, long offset, long length)
		{
			this.original = original;
			this.offset = offset;
			this.length = length;
			if (!original.CanRead)
			{
				throw new ArgumentException("The original stream must support reading!", nameof(original));
			}
			if (!original.CanSeek)
			{
				throw new ArgumentException("The original stream must support seeking!", nameof(original));
			}
			if (original.Length < offset + length || offset < 0 || length < 1)
			{
				throw new ArgumentOutOfRangeException($"Specified subsection is outside the bounds of the original stream!");
			}
		}
		public override void Flush()
		{
			throw new NotSupportedException();
		}
		public override int Read(byte[] buffer, int offset, int count)
		{
			var originalPosition = original.Position;
			if (Position < 0 || Position >= Length)
			{
				return 0;
			}
			if (Position + count >= Length)
			{
				count = Convert.ToInt32(Length - Position);
			}
			original.Seek(this.offset + Position, SeekOrigin.Begin);
			var bytesRead = original.Read(buffer, offset, count);
			Position += bytesRead;
			original.Seek(originalPosition, SeekOrigin.Begin);
			return bytesRead;
		}
		public override int ReadByte()
		{
			if (Position < 0 || Position >= Length)
			{
				return -1;
			}
			return base.ReadByte();
		}
		public override long Seek(long offset, SeekOrigin origin)
		{
			switch (origin)
			{
				case SeekOrigin.Begin:
					Position = offset;
					break;
				case SeekOrigin.Current:
					Position += offset;
					break;
				case SeekOrigin.End:
					Position = length + offset;
					break;
				default:
					throw new NotImplementedException($"Seeking from position '{origin}' is not implemented!");
			}
			return Position;
		}
		public override void SetLength(long value)
		{
			throw new NotSupportedException();
		}
		public override void Write(byte[] buffer, int offset, int count)
		{
			throw new NotSupportedException();
		}
	}
}