namespace Tinesware.Enumerables
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Should.Fluent;

    /// <summary>
    /// More extension methods for enumerables
    /// </summary>
    public static class Chunk
    {
        /// <summary>
        /// Split an IEnumerable<T> into an IEnumerable<T[]>
        /// </summary>
        /// <typeparam name="T">Element type of the array</typeparam>
        /// <param name="source">The IEnumerable to operate on</param>
        /// <param name="chunk">The window size</param>
        /// <returns>The enumeration</returns>
        public static IEnumerable<T[]> Window<T>(this IEnumerable<T> source, int chunk)
        {
            while (true)
            {
                var result = source.Take(chunk).ToArray();

                if (result.Any())
                {
                    yield return result;
                }
                else
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Turns a stream into an IEnumerable<byte>
        /// </summary>
        /// <param name="stream">The stream to wrap</param>
        /// <returns>The stream as an enumeration</returns>
        public static IEnumerable<byte> ToEnumerable(this Stream stream)
        {
            while (true)
            {
                var result = stream.ReadByte();
                if (result >= 0)
                {
                    yield return (byte)result;
                }
                else
                {
                    break;
                }
            }
        }

        /// <summary>
        /// A Self-test program
        /// </summary>
        private static void Main()
        {
            var input = Enumerable.Range(0, 42).Select(x => (byte)x).ToArray();
            var inStream = new MemoryStream(input);
            var channel = inStream.ToEnumerable();

            var chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)0);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)16);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(10);
            chunk[0].Should().Equal((byte)32);

            inStream.Position = 0;

            var chunks = inStream.ToEnumerable().Window(16).ToArray();
            chunks.Length.Should().Equal(3);
            chunks[0].Length.Should().Equal(16);
            chunks[1].Length.Should().Equal(16);
            chunks[2].Length.Should().Equal(10);
        }
    }
}