using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Apewer.Web
{

    internal sealed class MiniReader : Stream
    {

        bool _disposed = false;

        MiniConnection _connection;
        Stream _stream;
        long _remaining;

        internal byte[] RemainsBytes = null;
        int _offset = 0;
        long _total = 0L;

        internal MiniReader(MiniConnection connection, long remaining = -1)
        {
            _connection = connection;
            _stream = connection.Stream;
            _remaining = remaining;
        }

        public bool Disposed { get => _disposed; }

        public override bool CanRead { get => true; }

        public override bool CanWrite { get => false; }

        public override bool CanSeek { get => false; }

        public override long Length { get => throw new NotSupportedException(); }

        public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

        public override void Close() => _disposed = true;

        public override void Flush() { }

        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

        public override void SetLength(long value) => throw new NotSupportedException();

        #region read

        int ReadRemins(byte[] buffer, int offset, int count)
        {
            // 返回 0,读取 Remains 已经完成,由外层继续读取。
            if (RemainsBytes == null) return 0;
            var length = RemainsBytes.Length;
            if (length < 1) return 0;

            // Remains 中剩余的长度,并根据 count 参数限制读取数量。
            var result = length - offset;
            if (result < 1) return 0;
            if (result > count) result = count;

            // Content-Length 指定的剩余长度。
            if (_remaining > 0 && result > _remaining) result = (int)_remaining;

            // RemainsBytes 长度溢出限制。
            if (result + _offset > length) result = length - _offset;

            // 从 Remains 中读取,返回具体的字节数。
            Buffer.BlockCopy(RemainsBytes, _offset, buffer, offset, result);
            _offset += result;
            _remaining -= result;
            _total += result;

            // 已完成 Remains 的读取。
            if (_offset >= length) RemainsBytes = null;
            return result;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_connection.Server.SynchronousIO)
            {
                if (_disposed) throw new ObjectDisposedException(typeof(Stream).FullName);

                // 检查参数。
                if (buffer == null) throw new ArgumentNullException("参数 buffer 无效。");
                if (offset < 0) throw new ArgumentOutOfRangeException("offset", "参数 offset 小于 0。");
                if (count < 0) throw new ArgumentOutOfRangeException("count", "参数 count 小于 0。");
                int bufferLength = buffer.Length;
                if (offset > bufferLength) throw new ArgumentOutOfRangeException("offset", "指定的 offset 的值超出了 buffer 数组的容量。");
                if (offset + count > bufferLength) throw new ArgumentException("指定的 offset 和 count 超出了 buffer 数组的容量。");
                if (count == 0) return 0;

                // 检查剩余读取量。
                if (_remaining == 0) return 0;

                // 从 Remains 中读取。
                var nread = ReadRemins(buffer, offset, count);
                if (nread > 0) return nread;

                // 根据 Content-Length 限定长度。
                if (_remaining > 0 && _remaining < count) count = (int)_remaining;

                var read = _stream.Read(buffer, offset, count);
                if (read > 0 && _remaining > 0) _remaining -= read;
                return read;
            }
            else
            {
                var async = BeginRead(buffer, offset, count, null, null);
                var result = EndRead(async);
                return result;
            }
        }

        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback cback, object state)
        {
#if DEBUG
            if (_disposed) throw new ObjectDisposedException(typeof(Stream).FullName);

            // 检查参数。
            if (buffer == null) throw new ArgumentNullException("参数 buffer 无效。");
            if (offset < 0) throw new ArgumentOutOfRangeException("offset", "参数 offset 小于 0。");
            if (count < 0) throw new ArgumentOutOfRangeException("count", "参数 count 小于 0。");
            int bufferLength = buffer.Length;
            if (offset > bufferLength) throw new ArgumentOutOfRangeException("offset", "指定的 offset 的值超出了 buffer 数组的容量。");
            if (offset + count > bufferLength) throw new ArgumentException("指定的 offset 和 count 超出了 buffer 数组的容量。");

            // Content-Length
            if (_remaining == 0)
            {
                var ares = new MiniStreamAsyncResult();
                ares.Buffer = buffer;
                ares.Offset = offset;
                ares.Count = count;
                ares.Callback = cback;
                ares.State = state;
                ares.SynchRead = 0;
                ares.Complete();
                return ares;
            }
            if (_remaining > 0 && _remaining < count) count = (int)_remaining;

            return _stream.BeginRead(buffer, offset, count, cback, state);
#else
            throw new NotSupportedException();
#endif
        }

        public override int EndRead(IAsyncResult ar)
        {
#if DEBUG
            if (_disposed) throw new ObjectDisposedException(typeof(Stream).FullName);
            if (ar == null) throw new ArgumentNullException(nameof(ar));

            if (ar is MiniStreamAsyncResult)
            {
                var r = (MiniStreamAsyncResult)ar;
                if (!ar.IsCompleted) ar.AsyncWaitHandle.WaitOne();
                return r.SynchRead;
            }

            int nread = _stream.EndRead(ar);
            if (_remaining > 0 && nread > 0) _remaining -= nread;
            return nread;
#else
            throw new NotSupportedException();
#endif
        }

        #endregion

        #region write

        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => throw new NotSupportedException();

        public override void EndWrite(IAsyncResult ar) => throw new NotSupportedException();

        #endregion

    }

}