using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

namespace Cryville.Common.Network {
	public abstract class HttpResponseStream : Stream {
		public override bool CanRead { get { return true; } }

		public override bool CanSeek { get { return false; } }

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

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

		public override long Position {
			get { throw new NotSupportedException(); }
			set { throw new NotSupportedException(); }
		}
		
		public override void Flush() {
			// Do nothing
		}

		public abstract byte[] ReadToEnd();

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

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

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

	internal sealed class HttpResponseBlockStream : HttpResponseStream {
		readonly BinaryReader _reader;
		readonly int _length;
		int _pos = 0;
		internal HttpResponseBlockStream(BinaryReader reader, int length) {
			_reader = reader;
			_length = length;
		}
		public override int Read(byte[] buffer, int offset, int count) {
			int recv = 0;
			int recv_len = System.Math.Min(count, _length - _pos);
			if (recv_len == 0) return 0;
			while (recv < recv_len) {
				recv += _reader.Read(buffer, offset + recv, count - recv);
				Logger.Log("main", 0, "Network", "Message body received: {0}/{1}/{2}", recv, recv_len, _length);
			}
			_pos += recv_len;
			return recv_len;
		}
		public override byte[] ReadToEnd() {
			byte[] buffer = new byte[_length - _pos];
			Read(buffer, 0, buffer.Length);
			return buffer;
		}
	}

	internal sealed class HttpResponseChunkedStream : HttpResponseStream {
		readonly BinaryReader _reader;
		byte[] _chunk = null;
		int _pos = 0;
		internal HttpResponseChunkedStream(BinaryReader reader) {
			_reader = reader;
			ReadChunk();
		}
		public void ReadChunk() {
			if (_chunk != null && _chunk.Length == 0) return;
			string[] chunkHeader = HttpResponse.ReadLine(_reader).Split(';');
			// int chunkSize = Array.IndexOf(LEN, chunkHeader[0].ToLower()[0]);
			int chunkSize = int.Parse(chunkHeader[0], NumberStyles.HexNumber);
			if (chunkSize == -1)
				throw new IOException("Corrupted chunk received");
			if (chunkSize == 0) {
				_chunk = new byte[0];
				// TODO TE Header, now just discard
				var headers = new Dictionary<string, string>();
				while (HttpResponse.ParseHeader(_reader, headers)) ;
				return;
			}
			_chunk = new byte[chunkSize];
			int recv = 0;
			while (recv < chunkSize) {
				recv += _reader.Read(_chunk, recv, chunkSize - recv);
				Logger.Log("main", 0, "Network", "Message chunk received: {0}/{1}", recv, chunkSize);
			}
			_pos = 0;
			if (HttpResponse.ReadLine(_reader) != "")
				throw new IOException("Corrupted chunk received");
		}
		public override int Read(byte[] buffer, int offset, int count) {
			if (_chunk.Length == 0) return 0;
			int recv = 0;
			while (true) {
				if (count - recv <= _chunk.Length - _pos) break;
				Array.Copy(_chunk, _pos, buffer, recv, _chunk.Length - _pos);
				recv += _chunk.Length - _pos;
				ReadChunk();
				if (_chunk.Length == 0) return recv;
			}
			Array.Copy(_chunk, _pos, buffer, recv, count - recv);
			return count;
		}
		public override byte[] ReadToEnd() {
			if (_chunk.Length == 0) return new byte[0];
			List<byte[]> segs = new List<byte[]>();
			while (true) {
				if (_pos != 0) {
					var buffer = new byte[_chunk.Length - _pos];
					Array.Copy(_chunk, _pos, buffer, 0, buffer.Length);
					segs.Add(buffer);
				}
				else segs.Add(_chunk);
				ReadChunk();
				if (_chunk.Length == 0) {
					var result = new byte[segs.Sum(i => i.Length)];
					int p = 0;
					foreach (var i in segs) {
						Array.Copy(i, 0, result, p, i.Length);
						p += i.Length;
					}
					return result;
				}
			}
		}
	}
}