﻿// Copyright (c) Narvalo.Org. All rights reserved. See LICENSE.txt in the project root for license information.

namespace Narvalo.Tap.Sdk
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    public sealed class TestSet : IEnumerable<ITestCase>
    {
        private readonly HashSet<ITestCase> _set = new HashSet<ITestCase>();

        private int _failuresCount;

        public TestSet() { }

        public TestSet(int size)
        {
            Size = size;
        }

        public static TestSet Empty { get; } = new TestSet(0);

        public int TestsCount => _set.Count;

        public int FailuresCount => _failuresCount;

        public int? Size { get; }

        public bool BailedOut { get; private set; }

        public string BailOutReason { get; private set; }

        public bool Passed
            => !BailedOut && FailuresCount == 0 && (!Size.HasValue || TestsCount == Size.Value);

        public void BailOut()
        {
            BailedOut = true;
        }

        public void BailOut(string reason)
        {
            BailedOut = true;
            BailOutReason = reason ?? String.Empty;
        }

        public void Add(TestCase testCase)
        {
            Require.NotNull(testCase, nameof(testCase));

            ThrowIfBailedOut();

            if (!testCase.Passed) { _failuresCount++; }

            _set.Add(testCase);
        }

        // NB: A skipped test is never counted as failure.
        public void Add(SkippedTestCase testCase)
        {
            Require.NotNull(testCase, nameof(testCase));

            ThrowIfBailedOut();

            _set.Add(testCase);
        }

        // NB: A To-do test is never counted as failure.
        public void Add(TodoTestCase testCase)
        {
            Require.NotNull(testCase, nameof(testCase));

            ThrowIfBailedOut();

            _set.Add(testCase);
        }

        public IEnumerator<ITestCase> GetEnumerator() => _set.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        private void ThrowIfBailedOut()
        {
            if (BailedOut)
            {
                throw new InvalidOperationException();
            }
        }
    }
}
