using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace skyscraper8.SophtainerIO
{
	internal class OffsetStream : Stream
	{
		private readonly Stream wrapped;
		private readonly long offset;
		private readonly IDisposer disposer;

		public OffsetStream(Stream wrapped, long offset, IDisposer sophtainer)
		{
			this.wrapped = wrapped;
			this.offset = offset;
			this.disposer = sophtainer;
		}

		public override bool CanRead => wrapped.CanRead;

		public override bool CanSeek => wrapped.CanSeek;

		public override bool CanWrite => wrapped.CanWrite;

		public override long Length => wrapped.Length - offset;

		public override long Position 
		{ 
			get => wrapped.Position - offset;
			set => wrapped.Position = value + offset;
		}

		public override void Flush()
		{
			if (disposer.IsDisposed)
			{
				throw new ObjectDisposedException(disposer.ToString());
			}

			wrapped.Flush();
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			if (disposer.IsDisposed)
			{
				throw new ObjectDisposedException(disposer.ToString());
			}

			return wrapped.Read(buffer, offset, count);
		}

		public override long Seek(long offset, SeekOrigin origin)
		{
			if (disposer.IsDisposed)
			{
				throw new ObjectDisposedException(disposer.ToString());
			}

			switch (origin)
			{
				case SeekOrigin.Begin:
					wrapped.Seek(this.offset + offset, SeekOrigin.Begin); 
					break;
				case SeekOrigin.Current:
					wrapped.Seek(offset, SeekOrigin.Current);
					break;
				case SeekOrigin.End:
					wrapped.Seek(wrapped.Length -  offset, SeekOrigin.End);
					break;
				default:
					throw new NotImplementedException(origin.ToString());
			}
			return Position;
		}

		public override void SetLength(long value)
		{
			if (disposer.IsDisposed)
			{
				throw new ObjectDisposedException(disposer.ToString());
			}

			wrapped.SetLength(value + offset);
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			if (disposer.IsDisposed)
			{
				throw new ObjectDisposedException(disposer.ToString());
			}

			wrapped.Write(buffer, offset, count);
		}
	}
}
