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

namespace skyscraper8.Skyscraper.IO
{
	internal ref struct StreamlikeSpan
	{
		private ReadOnlySpan<byte> _readOnlySpan;

		public StreamlikeSpan(ReadOnlySpan<byte> readOnlySpan)
		{
			_readOnlySpan = readOnlySpan;
			internalLength = readOnlySpan.Length;
			internalPosition = 0;
		}

		private int internalLength, internalPosition;

		public int Read(byte[] buffer, int offset, int count)
		{
			int result = 0;
			for (int i = 0; i < count; i++)
			{
				buffer[i + offset] = _readOnlySpan[internalPosition];
				internalPosition++;
				result++;
			}

			return result;
		}

		public long Seek(long offset, SeekOrigin origin)
		{
			switch (origin)
			{
				case SeekOrigin.Begin:
					internalPosition = (int)offset;
					break;
				case SeekOrigin.Current:
					internalPosition += (int)offset;
					break;
				case SeekOrigin.End:
					internalPosition = internalLength + (int)offset;
					break;
			}

			return internalPosition;
		}

		public void SetLength(long value)
		{
			if (value > internalLength)
				throw new NotSupportedException("Can not lengthen Streamlikes. You have to shorten them.");

			_readOnlySpan = _readOnlySpan.Slice(0, (int)value);
			internalLength = (int)value;
		}

		public void Write(byte[] buffer, int offset, int count)
		{
			throw new NotSupportedException("Cannot write to an ReadOnlySpan");
		}

		public bool CanRead => true;
		public bool CanSeek => true;
		public bool CanWrite => true;
		public long Length => internalLength;
		public long Position => internalPosition;

		public byte ReadUInt8()
		{
			byte result = _readOnlySpan[internalPosition];
			internalPosition++;
			return result;
		}

		public ushort ReadUInt16BE()
		{
			ushort a = _readOnlySpan[internalPosition];
			a <<= 8;
			a += _readOnlySpan[internalPosition + 1];
			internalPosition += 2;
			return a;
		}

		public byte[] ReadBytes(int i)
		{
			byte[] buffer = new byte[i];
			if (Read(buffer, 0, i) != i)
			{
				throw new IOException(String.Format("Failed to read {0} bytes", i));
			}
			return buffer;
		}

		public ReadOnlySpan<byte> ReadSpan(int length)
		{
			ReadOnlySpan<byte> buffer = _readOnlySpan.Slice(internalPosition, length);
			internalPosition += length;
			return buffer;
		}

		public uint ReadUInt32BE()
		{
			uint result = _readOnlySpan[internalPosition];
			result <<= 8;
			result += _readOnlySpan[internalPosition + 1];
			result <<= 8;
			result += _readOnlySpan[internalPosition + 2];
			result <<= 8;
			result += _readOnlySpan[internalPosition + 3];
			internalPosition += 4;
			return result;
		}

		public int GetAvailableBytes()
		{
			return internalLength - internalPosition;
		}
	}

	class TelepathyStream : Stream
	{
		public override void Flush()
		{
			throw new NotImplementedException();
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			throw new NotImplementedException();
		}

		public override long Seek(long offset, SeekOrigin origin)
		{
			throw new NotImplementedException();
		}

		public override void SetLength(long value)
		{
			throw new NotImplementedException();
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			throw new NotImplementedException();
		}

		public override bool CanRead { get; }
		public override bool CanSeek { get; }
		public override bool CanWrite { get; }
		public override long Length { get; }
		public override long Position { get; set; }
	}
}
