namespace Caliburn.Micro.Core {
    using System;
    using System.Collections.Generic;
    /// 
    /// Used to gather the results from multiple child elements which may or may not prevent closing.
    /// 
    /// The type of child element.
    public class DefaultCloseStrategy : ICloseStrategy {
        readonly bool closeConductedItemsWhenConductorCannotClose;
        /// 
        /// Creates an instance of the class.
        /// 
        /// Indicates that even if all conducted items are not closable, those that are should be closed. The default is FALSE.
        public DefaultCloseStrategy(bool closeConductedItemsWhenConductorCannotClose = false) {
            this.closeConductedItemsWhenConductorCannotClose = closeConductedItemsWhenConductorCannotClose;
        }
        /// 
        /// Executes the strategy.
        /// 
        /// Items that are requesting close.
        /// The action to call when all enumeration is complete and the close results are aggregated.
        /// The bool indicates whether close can occur. The enumerable indicates which children should close if the parent cannot.
        public void Execute(IEnumerable toClose, Action> callback) {
            using (var enumerator = toClose.GetEnumerator()) {
                Evaluate(new EvaluationState(), enumerator, callback);
            }
        }
        void Evaluate(EvaluationState state, IEnumerator enumerator, Action> callback) {
            var guardPending = false;
            do {
                if (!enumerator.MoveNext()) {
                    callback(state.FinalResult, closeConductedItemsWhenConductorCannotClose ? state.Closable : new List());
                    break;
                }
                var current = enumerator.Current;
                var guard = current as IGuardClose;
                if (guard != null) {
                    guardPending = true;
                    guard.CanClose(canClose => {
                        guardPending = false;
                        if (canClose) {
                            state.Closable.Add(current);
                        }
                        state.FinalResult = state.FinalResult && canClose;
                        if (state.GuardMustCallEvaluate) {
                            state.GuardMustCallEvaluate = false;
                            Evaluate(state, enumerator, callback);
                        }
                    });
                    state.GuardMustCallEvaluate = state.GuardMustCallEvaluate || guardPending;
                } else {
                    state.Closable.Add(current);
                }
            } while (!guardPending);
        }
        class EvaluationState {
            public readonly List Closable = new List();
            public bool FinalResult = true;
            public bool GuardMustCallEvaluate;
        }
    }
}