using BoFilTest.Domain;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BoFilTest.Tests
{
    [TestFixture]
    class TestStepListTest
    {
        [Test]
        public void SimplestStepTypesMatch()
        {
            var spec = new FakeTestSpec(TimeSpan.Zero, 0, TimeSpan.Zero, TimeSpan.Zero, 1.0, false);
            AssertSpec(new List<StepType>{ StepType.Pause, StepType.CakeFormation, StepType.Demoisturizing }, spec);
        }

        // Following 4 tests are directly from the examples given by Bokela
        [Test]
        public void Example1StepTypesMatch()
        {
            var spec = new FakeTestSpec(TimeSpan.Zero, 0, TimeSpan.Zero, TimeSpan.Zero, 1.0, true);
            AssertSpec(new List<StepType> { StepType.Pause, StepType.CakeFormation, StepType.GasBreakthrough, StepType.Demoisturizing }, spec);
        }

        [Test]
        public void Example2StepTypesMatch()
        {
            var spec = new FakeTestSpec(TimeSpan.FromSeconds(10), 2, TimeSpan.Zero, TimeSpan.Zero, 1.0, false);
            AssertSpec(new List<StepType> { StepType.Pause, StepType.CakeFormation,
                StepType.IntermediateDemoisturizing, StepType.Pause, StepType.Washing,
                StepType.IntermediateDemoisturizing, StepType.Pause, StepType.Washing,
                StepType.Demoisturizing }, spec);
        }

        [Test]
        public void Example3StepTypesMatch()
        {
            var spec = new FakeTestSpec(TimeSpan.FromSeconds(42), 1, TimeSpan.FromSeconds(33), TimeSpan.Zero, 1.0, false);
            AssertSpec(new List<StepType> { StepType.Pause, StepType.CakeFormation,
                StepType.IntermediateDemoisturizing, StepType.Pause, StepType.Washing,
                StepType.IntermediateDemoisturizing, StepType.Pause, StepType.Pressing,
                StepType.Pause, StepType.Demoisturizing
            }, spec);
        }

        [Test]
        public void Example4StepTypesMatch()
        {
            var spec = new FakeTestSpec(TimeSpan.FromSeconds(42), 0, TimeSpan.Zero, TimeSpan.FromSeconds(33), 1.0, false);
            AssertSpec(new List<StepType> {StepType.Pause, StepType.CakeFormation, StepType.IntermediateDemoisturizing,
                StepType.Pause, StepType.Vaporization, StepType.Demoisturizing}, spec);
        }

        [Test]
        public void PauseEvenWithoutIntermediateDemoisturization()
        {
            var spec = new FakeTestSpec(TimeSpan.Zero, 1, TimeSpan.Zero, TimeSpan.Zero, 1.0, false);
            AssertSpec(new List<StepType> { StepType.Pause, StepType.CakeFormation, StepType.Pause, StepType.Washing, StepType.Demoisturizing }, spec);
        }

        [Test]
        public void NotIntermediateDemoisturizationWithoutExtraSteps()
        {
            var stepList = TestStepList.Build(new FakeTestSpec(TimeSpan.FromSeconds(1), 0, TimeSpan.Zero, TimeSpan.Zero, 1.0, false));
            Assert.IsEmpty(stepList.Where(x => x.Description.Type == StepType.IntermediateDemoisturizing));
        }

        [Test]
        public void HasIntermediateDemoisturizingWithPressing()
        {
            var stepList = TestStepList.Build(new FakeTestSpec(TimeSpan.FromSeconds(1), 0, TimeSpan.FromSeconds(0.5), TimeSpan.Zero, 1.0, false));
            Assert.IsNotEmpty(stepList.Where(x => x.Description.Type == StepType.IntermediateDemoisturizing));
        }

        [Test]
        public void HasIntermediateDemoisturizingWithVaporization()
        {
            var stepList = TestStepList.Build(new FakeTestSpec(TimeSpan.FromSeconds(1), 0, TimeSpan.Zero, TimeSpan.FromSeconds(0.5), 1.0, false));
            Assert.IsNotEmpty(stepList.Where(x => x.Description.Type == StepType.IntermediateDemoisturizing));
        }

        [Test]
        public void HasTwoIntermediateDemoisturizingsWithWashingAndPressing()
        {
            var stepList = TestStepList.Build(new FakeTestSpec(TimeSpan.FromSeconds(1), 1, TimeSpan.FromSeconds(3), TimeSpan.Zero, 1.0, false));
            Assert.AreEqual(stepList.Where(x => x.Description.Type == StepType.IntermediateDemoisturizing).Count(), 2);
        }

        [Test]
        public void HasNoIntermediateDemoisturizingBeforeDemoisturizing()
        {
            var stepList = TestStepList.Build(new FakeTestSpec(TimeSpan.FromSeconds(1), 1, TimeSpan.Zero, TimeSpan.Zero, 1.0, false))
                .Where(x => x.Description.Type != StepType.Pause).ToList();// Remove pause for this
            var lastIntermediateDemoisturizing = stepList.FindLastIndex(step => step.Description.Type == StepType.IntermediateDemoisturizing);
            Assert.AreEqual(StepType.Demoisturizing, stepList.Last().Description.Type);
            Assert.AreNotEqual(stepList.Count, lastIntermediateDemoisturizing + 2);
        }

        void AssertSteps(List<StepType> expected, List<TestStep> actual)
        {
            Assert.That(actual.Select(x => x.Description.Type), Is.EquivalentTo(expected));
        }

        void AssertSpec(List<StepType> expected, ITestSpecification spec)
        {
            var stepList = TestStepList.Build(spec);
            AssertSteps(expected, stepList);
        }

        class FakeTestSpec : ITestSpecification
        {
            public FakeTestSpec(TimeSpan IntermediateDemoisturizingTime, int WashingPassCount, TimeSpan Pressing, TimeSpan Vaporization, double DemoisturizationRatio, bool UseGasBreakthrough)
            {
                this.IntermediateDemoisturizingTime = IntermediateDemoisturizingTime;
                this.WashingPassCount = WashingPassCount;
                this.Pressing = Pressing;
                this.Vaporization = Vaporization;
                this.DemoisturizationRatio = DemoisturizationRatio;
                this.UseGasBreakthrough = UseGasBreakthrough;
            }

            public int WashingPassCount { get; }
            public TimeSpan Pressing { get; }
            public TimeSpan Vaporization { get; }
            public double DemoisturizationRatio { get; }
            public bool UseGasBreakthrough { get; }
            public TimeSpan IntermediateDemoisturizingTime { get; }
        }
        
    }
}
