using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Cache;
using MarketData.Utils;

namespace MarketData.Generator.Indicators
{
// ***********************************************************************************
  public class BandBreakIndicator
  {
    private BandBreakIndicator()              
    {
    }
// This Indicator determines if a high price exceeds the K band (+2SSD) of the bollinger band over the past "dayCount" days.
// If high price breaks the K band then we consider that to be a true indication.
    public static bool IsUpperBandBreakHigh(String symbol,DateTime pricingDate,int dayCount=20) 
    {
      bool isBandBreak=false;
      int pricingDays=dayCount*4;
      if(pricingDays<60)pricingDays=60;
      Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,pricingDays);
      if(null==prices || 0==prices.Count)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*** IsUpperBandBreakHigh: Failed to retrieve prices for {0} on {1} for {2} days. ***",symbol,pricingDate.ToShortDateString(),pricingDays));
        return isBandBreak;
      }
      BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
      if(null==bollingerBands)return false;
      bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
      BollingerBandElement item=bollingerBands.Where(x => x.High > x.K).FirstOrDefault();
      if(null!=item)isBandBreak=true;
      return isBandBreak;
    }
// This Indicator determines if a close price is equal to or less than the L band (-2SSD) of the bollinger band over the past "dayCount" days.
// If close price breaks or is equal to the L band then we consider that to be a true indication.
// If there is a violation then the date of the violation is return otherwise we return Epoch
    public static DateTime IsLowerBandBreakClose(String symbol,DateTime pricingDate,int dayCount=20) 
    {
      DateTime bandBreakDate=Utility.Epoch;
      int pricingDays=dayCount*4;
      if(pricingDays<60)pricingDays=60;
      Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,pricingDays);
      if(null==prices || 0==prices.Count)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*** IsLowerBandBreakClose: Failed to retrieve prices for {0} on {1} for {2} days. ***",symbol,pricingDate.ToShortDateString(),pricingDays));
        return bandBreakDate;
      }
      BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
      if(null==bollingerBands || bollingerBands.Count<dayCount)return bandBreakDate;
      bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
      BollingerBandElement item=bollingerBands.Where(x => x.Close <= x.L).FirstOrDefault();
      if(null!=item)
      {
        bandBreakDate=item.Date;
      }
      return bandBreakDate;
    }
  }
}