﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

namespace DaveSquared.Workshop.Tests
{
	[TestFixture]
	public class RangeSpec
	{

		[Test]
		public void Should_be_able_to_get_enumerator_with_linq()
		{
			List<int> rangeValues = new List<int>();
			var range = (from number in new int[] { 1, 2 } select number);
			foreach (int i in range)
			{
				rangeValues.Add(i);
			}
			Assert.That(rangeValues, Is.EqualTo(new int[] { 1, 2 }));
		}

		[Test]
		public void Should_be_able_to_directly_apply_function_with_linq()
		{
			List<int> rangeValues = new List<int>();
			(from number in new int[] { 1, 2 } select number)
				.ToList()
				.ForEach(i => rangeValues.Add(i));
			Assert.That(rangeValues, Is.EqualTo(new int[] { 1, 2 }));
		}

		[Test]
		public void Should_be_able_to_get_enumerator()
		{
			List<int> rangeValues = new List<int>();
			foreach (int i in Range.From(1).To(2))
			{
				rangeValues.Add(i);
			}
			Assert.That(rangeValues, Is.EqualTo(new int[] { 1, 2 }));
		}


		[Test]
		public void Should_be_able_to_do_sum_with_range()
		{
			int sum = 0;
			Range.From(1).To(5).Do(i => sum += i);
			Assert.That(sum, Is.EqualTo(1 + 2 + 3 + 4 + 5));
		}

		[Test]
		public void Should_be_able_to_use_linq_with_range()
		{
			String oneToThree = "";
			foreach (int num in Range.From(1).To(5).Where(i => i <= 3))
			{
				oneToThree = oneToThree + num + " ";
			}
			Assert.That(oneToThree, Is.EqualTo("1 2 3 "));
		}

		[Test]
		public void Should_be_able_to_get_range_from_1_to_10()
		{
			Assert.That(Range.From(1).To(10).ToArray(), Is.EqualTo(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }));
		}

		[Test]
		public void Should_be_able_to_get_range_from_10_to_1()
		{
			Assert.That(Range.From(10).To(1).ToArray(), Is.EqualTo(new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }));
		}

		[Test]
		public void Should_be_able_to_set_a_step_value()
		{
			Assert.That(Range.From(1).To(10).WithStepSize(2).ToArray(), Is.EqualTo(new int[] { 1, 3, 5, 7, 9 }));
		}

		[Test]
		public void Should_handle_step_values_that_immediately_go_out_of_range()
		{
			Assert.That(Range.From(1).To(10).WithStepSize(-1).ToArray(), Is.EqualTo(new int[] { 1 }));
			Assert.That(Range.From(10).To(1).WithStepSize(2).ToArray(), Is.EqualTo(new int[] { 10 }));
			Assert.That(Range.From(-10).To(1).WithStepSize(-1).ToArray(), Is.EqualTo(new int[] { -10 }));
		}

		[Test]
		public void Should_be_able_to_produce_ranges_of_negative_numbers()
		{
			Assert.That(Range.From(-5).To(-10).ToArray(), Is.EqualTo(new int[] { -5, -6, -7, -8, -9, -10 }));
			Assert.That(Range.From(-10).To(-5).ToArray(), Is.EqualTo(new int[] { -10, -9, -8, -7, -6, -5 }));
		}

		[Test]
		public void Test_original_mocking_scenario()
		{
			MockRepository mockery = new MockRepository();
			IPagerRenderer renderer = mockery.CreateMock<IPagerRenderer>();
			using (mockery.Record())
			{
				renderer.WriteCurrentPage(1);
				renderer.WritePageLink(2);
				renderer.WritePageLink(3);
				renderer.WritePageLink(4);
				renderer.WritePageLink(5);
			}
			using (mockery.Playback())
			{
				renderer.WriteCurrentPage(1);
				Range.From(2).To(5).Do(i => renderer.WritePageLink(i));
			}
		}

	}

	public interface IPagerRenderer
	{
		void WriteCurrentPage(int i);
		void WritePageLink(int i);
	}

	public class Range : IEnumerable<int>
	{
		private readonly int start;
		private int stop;
		private int step;

		public Range(int start)
		{
			this.start = stop = start;
		}
		public static Range From(int startRange)
		{
			return new Range(startRange);
		}

		public Range To(int endRange)
		{
			stop = endRange;
			return this;
		}

		public Range WithStepSize(int step)
		{
			this.step = step;
			return this;
		}

		public void Do(Action<int> f)
		{
			foreach (int i in this)
			{
				f(i);
			}
		}

		public IEnumerator<int> GetEnumerator()
		{
			int max = Math.Max(start, stop);
			int min = Math.Min(start, stop);
			if (step == default(int))
			{
				step = (start == min) ? 1 : -1;
			}
			int current = start;
			while (current >= min && current <= max)
			{
				yield return current;
				current += step;
			}
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}
