using MarketData.Cache;
using MarketData.MarketDataModel;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MarketData.Generator.MovingAverage
{
    public static class ExponentialMovingAverageCrossover
    {
      public static MovingAverageCrossovers GetMovingAverageCrossovers(String symbol, DateTime analysisDate,double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41)
      {
        Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, analysisDate, slowCrossoverDays * 6);
        if(prices == null || 0==prices.Count)return new MovingAverageCrossovers();

        return GetMovingAverageCrossovers(prices, threshholdPercentDecimal, fastCrossOverDays, slowCrossoverDays);  
      }

      /// <summary>
      /// GetMovingAverageCrossovers  
      /// Prices will be in descending order with the most recent price at index[0] and the least recent price at index[count-1]
      /// </summary>
      /// <param name="prices"></param>
      /// <param name="fastCrossOverDays">The fast crossover.  Tom Basso uses 9</param>
      /// <param name="slowCrossoverDays">The slow crosser.  Tom Basso uses 41</param>
      /// <param name="threshholdPercent">The threshhold percent.  5% would be .05</param>
      /// <returns></returns>
      public static MovingAverageCrossovers GetMovingAverageCrossovers(Prices prices, double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41)
      {
        MovingAverageCrossovers movingAverageCrossovers = new MovingAverageCrossovers();

        if(prices == null || 0==prices.Count || prices.Count<=slowCrossoverDays)return movingAverageCrossovers;
        if(fastCrossOverDays >= slowCrossoverDays)return movingAverageCrossovers;

        DMAPrices fastDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, fastCrossOverDays);
        DMAPrices slowDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, slowCrossoverDays);

        if(null == fastDMAPrices || 0 == fastDMAPrices.Count)return movingAverageCrossovers;
        if(null == slowDMAPrices || 0 == slowDMAPrices.Count)return movingAverageCrossovers;

        FindCrossovers(fastDMAPrices, slowDMAPrices, movingAverageCrossovers, threshholdPercentDecimal);

// put the most recent item in the lowest index
        movingAverageCrossovers = new MovingAverageCrossovers((movingAverageCrossovers as List<MovingAverageCrossover>).Where(x => x.IsChangeInTrend==true).OrderByDescending(x => x.EventDate).ToList());
        return movingAverageCrossovers;
      }

      /// <summary>
      /// If the sorter term (faster) crossed the longer term (slower) moving average then a direction change is noted. 
      /// If the change carried more than 5% in that direction then consider it a successful trend
      /// </summary>
      /// <param name="fastDMAPrices"></param>
      /// <param name="slowDMAPrices"></param>
      /// <param name="movingAverageCrossovers"></param>
      private static void FindCrossovers(DMAPrices fastDMAPrices, DMAPrices slowDMAPrices,MovingAverageCrossovers movingAverageCrossovers, double threshholdPercentDecimal) 
      {
        double trendChangePercentThreshhold = threshholdPercentDecimal*100.00;                      // if a change in direction carries more than 5% then consider is a successful trend
        movingAverageCrossovers.Clear();
        DateTime startDate =  slowDMAPrices[slowDMAPrices.Count-1].Date;
        MovingAverageCrossover movingAverageCrossover = default;

        DMAPricesByDate fastDMAPricesByDate = fastDMAPrices.GetDMAPricesByDate();
        DMAPricesByDate slowDMAPricesByDate = slowDMAPrices.GetDMAPricesByDate();

        List<DateTime> availableDates = new List<DateTime>(slowDMAPricesByDate.Keys);

        availableDates.Sort((a, b) => DateTime.Compare(a,b));   // earliest date should be in the lowest index, most recent date should be in the highest  2024[0], 2025[1], 2026[2] for example

        int currentDirection=0;     // 1:fast DMA is above the slow,  -1:fast DMA is below the slow,  0:slow DMA == fastDMA

        DMAPrice prevSlowDMAPrice = default;
        DMAPrice prevFastDMAPrice = default;

        for(int index=0;index<availableDates.Count;index++)
        {
          DateTime analysisDate = availableDates[index];
          DMAPrice slowDMAPrice = slowDMAPricesByDate[analysisDate];
          DMAPrice fastDMAPrice = fastDMAPricesByDate[analysisDate];

          if(index == 0)  // establish an initial orientation of the fast and slow moving averages.  We need to create a new record to have a current trend
          {
            if(fastDMAPrice.AVGPrice > slowDMAPrice.AVGPrice)
            {
              movingAverageCrossover = new MovingAverageCrossover();
              movingAverageCrossover.EventDate = analysisDate;
              movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
              movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpStart;
              movingAverageCrossover.IsChangeInTrend = false;
              movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
              movingAverageCrossover.FastDMAPrice = fastDMAPrice;
              movingAverageCrossover.ChangePercent=0.00;
              movingAverageCrossovers.Add(movingAverageCrossover);
              currentDirection = 1;
            }
            else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice)
            {
              movingAverageCrossover = new MovingAverageCrossover();
              movingAverageCrossover.EventDate = analysisDate;
              movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
              movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownStart;
              movingAverageCrossover.IsChangeInTrend = false;
              movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
              movingAverageCrossover.FastDMAPrice = fastDMAPrice;
              movingAverageCrossover.ChangePercent=0.00;
              movingAverageCrossovers.Add(movingAverageCrossover);
              currentDirection = -1;
            }
            else 
            {
              movingAverageCrossover = new MovingAverageCrossover();
              movingAverageCrossover.EventDate = analysisDate;
              movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
              movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.NeutralStart;
              movingAverageCrossover.IsChangeInTrend = false;
              movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
              movingAverageCrossover.FastDMAPrice = fastDMAPrice;
              movingAverageCrossover.ChangePercent=0.00;
              movingAverageCrossovers.Add(movingAverageCrossover);
              currentDirection = 0;
            }
            prevSlowDMAPrice = slowDMAPrice;
            prevFastDMAPrice = fastDMAPrice;
          }
          else
          {
            if(fastDMAPrice.AVGPrice > slowDMAPrice.AVGPrice)
            {
              if(1 == currentDirection)  // The fast is above the slow as it was previously 
              {
                if(movingAverageCrossovers.Count>0)    // check the last change record for a threshhold break
                {
                  MovingAverageCrossover movingAverageCrossOver = movingAverageCrossovers[movingAverageCrossovers.Count-1];
                  double changePercent = ((fastDMAPrice.AVGPrice - movingAverageCrossOver.FastDMAPrice.AVGPrice)/movingAverageCrossOver.FastDMAPrice.AVGPrice)*100.00;
                  if(changePercent > 0.00 && changePercent > trendChangePercentThreshhold)    // if the threshhold is exceeded then create a new record and mark it as a trend change
                  {
                    movingAverageCrossover = new MovingAverageCrossover();
                    movingAverageCrossover.EventDate = analysisDate;
                    movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
                    movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
                    movingAverageCrossover.IsChangeInTrend = true;
                    movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
                    movingAverageCrossover.FastDMAPrice = fastDMAPrice;
                    movingAverageCrossover.ChangePercent=changePercent;
                    movingAverageCrossovers.Add(movingAverageCrossover);
                  }
                }
              }
              else if(-1 == currentDirection || 0 == currentDirection)  // The fast is above the slow and it was below the slow previously
              {
                currentDirection = 1;          // switch directions
                movingAverageCrossover = new MovingAverageCrossover();
                movingAverageCrossover.EventDate = analysisDate;
                movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
                movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
                movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
                movingAverageCrossover.FastDMAPrice = fastDMAPrice;
                double changePercent =  Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00);
                movingAverageCrossover.ChangePercent=changePercent;
                if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true;
                else movingAverageCrossover.IsChangeInTrend = false;
                movingAverageCrossovers.Add(movingAverageCrossover);
              }
            }
            else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice)
            {
              if(-1 == currentDirection)       // The fast is below the slow as it was previously 
              {
                if(movingAverageCrossovers.Count>0)    // check the last change record for a threshhold break
                {
                  movingAverageCrossover = movingAverageCrossovers[movingAverageCrossovers.Count-1];
                  double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - movingAverageCrossover.FastDMAPrice.AVGPrice)/movingAverageCrossover.FastDMAPrice.AVGPrice)*100.00);
                  if(changePercent<0.00 && Math.Abs(changePercent)>trendChangePercentThreshhold)    // if the threshhold is exceeded then create a new record and mark it as a trend change
                  {
                    movingAverageCrossover = new MovingAverageCrossover();
                    movingAverageCrossover.EventDate = analysisDate;
                    movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
                    movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
                    movingAverageCrossover.IsChangeInTrend = true;
                    movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
                    movingAverageCrossover.FastDMAPrice = fastDMAPrice;
                    movingAverageCrossover.ChangePercent = changePercent;
                    movingAverageCrossovers.Add(movingAverageCrossover);
                  }
                }
              }
              else if(1 == currentDirection || 0 == currentDirection)    // The fast is below the slow but it was above the slow previously so we've crossed
              {
                currentDirection = -1;          // switch directions
                movingAverageCrossover = new MovingAverageCrossover();
                movingAverageCrossover.EventDate = analysisDate;
                movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
                movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownCross;
                movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
                movingAverageCrossover.FastDMAPrice = fastDMAPrice;
                double changePercent =  Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00);
                movingAverageCrossover.ChangePercent=changePercent;
                if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true;
                else movingAverageCrossover.IsChangeInTrend = false;
                movingAverageCrossovers.Add(movingAverageCrossover);
              }
            }
            else // The fast and the slow are equal.  There's nothing to do here
            {

            }
            prevSlowDMAPrice = slowDMAPrice;
            prevFastDMAPrice = fastDMAPrice;
          }
        }
      }
    }
}
