using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Indicators.Trend;
using StockSharp.Algo;
using StockSharp.Algo.Logging;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using Ecng.Common;
using Ecng.Collections;
namespace ra81.Algo.Strategies
{
    public class BasketProtectionStrategy : Strategy, IProtectionStrategy
    {
        private object _globalLock = new object();
        /// 
        /// Защищаемая сделка.
        /// 
        public MyTrade ProtectedTrade { get; protected set; }
        /// 
        /// Текущее состояние стратегии, активирована она лии нет.
        /// 
        public bool IsActivated { get; protected set; }
        /// 
        /// Стратегия которая активировалась первой среди защитных.
        /// 
        public IProtectionStrategy FirstActivated { get; protected set; }
        /// 
        /// Событие вызывается сразу же как только защитная стратегия активировалась
        /// и начала закрывать позицию.
        /// 
        public event Action Activated;
        public BasketProtectionStrategy()
        {
            ProtectedTrade = null;
            IsActivated = false;
            
            EnableRulesLog = true;
            
            ChildStrategies.Adding += ChildStrategiesOnAdding;
        }
        /// 
        /// Метод вызывается тогда, когда вызвался метод , и состояние  перешло в значение .
        /// 
        protected override void OnStarting()
        {
            if (ChildStrategies.Count == 0)
                throw new InvalidOperationException("Дочерние стратегии отсутствуют, невозможно запустить комплесную защитную стратегию.");
            base.OnStarting();
        }
        protected override void OnStopping()
        {
            Debug.WriteLine("OnStopping: Число правил {0}".Put(Rules.Count));
            this.AddInfoLog("Очищая список правил стратегии.");
            Rules.Clear();
            this.AddInfoLog("Останавливаю все дочерние стратегии.");
            ChildStrategies.ToArray().ForEach(s => s.Stop());
 
            base.OnStopping();
        }
        
        
        /// 
        /// Метод вызывается при добавлении дочерней стратегии. Проверяет параметры стратегии и вешается на событие .
        /// 
        /// Добавляемая защитная стратегия. Должна соответствовать интерфейсу 
        private void ChildStrategiesOnAdding(Strategy strategy)
        {
            // проверяем чтобы защитная стратегия была, а не какая-то другая.
            if (!(strategy is IProtectionStrategy))
                throw new InvalidOperationException("Дочерней может быть только защитная стратегия реализующая IProtectionStrategy.");
            var protectionStrategy = strategy as IProtectionStrategy;
            // проверяем чтобы защитные стратегии были для одной сделки. Если защищаемая сделка еще не определена, берем из добавляемой стратегии.
            ProtectedTrade = ProtectedTrade ?? protectionStrategy.ProtectedTrade;
            if (ProtectedTrade != protectionStrategy.ProtectedTrade)
                throw new InvalidOperationException("Добавляемые защитные стратегии должны защищать одну и ту же сделку.");
            // как только активировалась защита одной из дочерних стратегий, сразу же активируем защиту комплексной стратегии.
            this
                .When(protectionStrategy.ProtectionActivated())
                .Do(s => Activate(s) );
            Debug.WriteLine("OnAdding: Число правил {0}".Put(Rules.Count));
        }
        /// 
        /// Активирует защиту сделки, то есть начинает процедуру защиты.
        /// Если защита уже активирована, то ничего не делает. Состояние защиты проверяется 
        /// через свойство . В случае активации генерирует событие 
        /// 
        /// Стратегия которая активировалась и вызвала активацию комплексной защитной стратегии. 
        protected virtual void Activate(IProtectionStrategy strategy)
        {
            // защита от двойного входа в состояние защиты.
            lock (_globalLock)
            {
                if (IsActivated) return;
                IsActivated = true;
                FirstActivated = strategy;
            }
            // после остановки стратегии завершим базовую стратегию тоже.
            var s = strategy as Strategy;
            //this
            //    .When(s.Stopped())
            //    .Do(Stop);
            Debug.WriteLine("Activate: Число правил {0}".Put(Rules.Count));
            this.AddInfoLog("Активировалась одна из дочерних стратегий защиты, завершаем остальные стратегии защиты.");
            RaiseActivatedEvent();
            
            // очищаем все правила стратегии и останавливаем все дочерние кроме активной. Когда активная тоже остановится, защитная стратегия тоже остановится.
            //Rules.Clear();
            StopNotActiveChilds();
        }
        /// 
        /// Останавливает все дочерние защитные стратегии для сделки, кроме той которая активировалась.
        /// 
        private void StopNotActiveChilds()
        {
            var firstActivated = FirstActivated as Strategy;
            var stoppingStrategies = ChildStrategies.Where(s => s != firstActivated).ToArray();
            Debug.WriteLine("Нужно остановить {0} дочерних".Put(stoppingStrategies.Count()));
            stoppingStrategies.ForEach(s => Stop());
        }
        /// 
        /// Сгенерировать событие об активации защиты.
        /// 
        protected virtual void RaiseActivatedEvent()
        {
            Activated.SafeInvoke();
        }
        protected override void DisposeManaged()
        {
            ChildStrategies.Adding -= ChildStrategiesOnAdding;
            base.DisposeManaged();
        }
    }
}