using Moq;
using project.domain.hierarchy;
using project.domain.payroll;

namespace unit.domain.hierarchy
{
    public class HierarchySpecs
    {
        public abstract class concern : runner<Hierarchy>
        {
            protected override Hierarchy create_sut()
            {
                return new Hierarchy();
            }
        }

        [Concern(typeof (Hierarchy))]
        public class when_visiting_item_in_a_hierarchy : concern
        {
            context c = () =>
            {
                visitor = new Mock<Visitor<Hierarchy>>();
                middle = new Hierarchy();
                bottom = new Hierarchy();
            };

            because b = () =>
            {
                top = sut;
                top.add(middle);
                middle.add(bottom);
                top.accept(visitor.Object);
            };

            it should_visit_everyone = () =>
            {
                visitor.Verify(x => x.visit(top));
                visitor.Verify(x => x.visit(top));
                visitor.Verify(x => x.visit(middle));
                visitor.Verify(x => x.visit(bottom));
            };

            static Mock<Visitor<Hierarchy>> visitor;
            static Hierarchy top;
            static Hierarchy middle;
            static Hierarchy bottom;
        }

        [Concern(typeof (Hierarchy))]
        public class when_moving_a_sub_tree_from_one_tree_to_another : concern
        {
            context c = () =>
            {
                old_tree = new Hierarchy();
                new_tree = new Hierarchy();
                child = new Hierarchy();
            };

            because b = () =>
            {
                old_tree.add(child);
                child.move_to(new_tree);
            };

            it should_remove_the_sub_tree_from_the_old_tree = () =>
            {
                old_tree.contains(child).should_be_false();
                child.belongs_to(old_tree).should_be_false();
            };

            it should_add_the_sub_tree_to_the_new_one = () =>
            {
                new_tree.contains(child).should_be_true();
                child.belongs_to(new_tree).should_be_true();
            };

            static Hierarchy old_tree;
            static Hierarchy new_tree;
            static Hierarchy child;
        }

        [Concern(typeof (Hierarchy))]
        public class when_removing_a_descendant_from_a_tree_that_it_does_not_belong_to : concern
        {
            context c = () =>
            {
                orphan = new Hierarchy();
            };

            because b = () =>
            {
                sut.remove(orphan);
            };

            it should_ignore_the_descdendant = () =>
            {
                sut.contains(orphan).should_be_false();
                orphan.belongs_to(sut).should_be_false();
            };

            static Hierarchy orphan;
        }

        [Concern(typeof (Hierarchy))]
        public class when_moving_a_child_to_another_tree_but_does_not_already_belong_to_a_tree : concern
        {
            context c = () =>
            {
                orphan = new Hierarchy();
            };

            because b = () =>
            {
                orphan.move_to(sut);
            };

            it should_move_the_orphan_to_the_new_family = () =>
            {
                sut.contains(orphan).should_be_true();
                orphan.belongs_to(sut).should_be_true();
            };

            static Hierarchy orphan;
        }
    }
}