using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System.Linq;
using MarketData.Numerical;
using System.Text;

namespace MarketData.Generator.MGSHMomentum 
{
    public class SlopeManager
    {
      private double bandSlopeSpreadKL5;
      private double bandSlopeSpreadKL10;
      private double bandSlopeSpreadKL30;
      private double bandSlopeSpreadKL60;
      private double bandSlopeSpreadKL90;

      public SlopeManager(BollingerBandElementsByDate bollingerBandElementsByDate, DateTime analysisDate)
      {
          bandSlopeSpreadKL5 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 5);
          bandSlopeSpreadKL10 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 10);
          bandSlopeSpreadKL30 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 30);
          bandSlopeSpreadKL60 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 60);
          bandSlopeSpreadKL90 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 90);
      }

      public bool Verbose { get; set; } = true;

    /// <summary>
    /// The pattern follows.. 90, 60, 30, 10 ,5 
    /// </summary>
    /// <param name="bandStringKLSpread">++0-0</param>
    /// <returns></returns>
      public bool IsMatchKLSpread(String bandStringKLSpread)
      {
        StringBuilder sbKLSPread = new StringBuilder();
        sbKLSPread.Append(GetValue(bandSlopeSpreadKL90)).Append(GetValue(bandSlopeSpreadKL60)).Append(GetValue(bandSlopeSpreadKL30)).Append(GetValue(bandSlopeSpreadKL10)).Append(GetValue(bandSlopeSpreadKL5));
        return AreEqual(bandStringKLSpread, sbKLSPread.ToString());
      }

      public String GetKLSpread()
      {
        StringBuilder sbKLSPread = new StringBuilder();
        sbKLSPread.Append(GetValue(bandSlopeSpreadKL90)).Append(GetValue(bandSlopeSpreadKL60)).Append(GetValue(bandSlopeSpreadKL30)).Append(GetValue(bandSlopeSpreadKL10)).Append(GetValue(bandSlopeSpreadKL5));
        return sbKLSPread.ToString();
      }

      public int FindFirstMatchRtoL(String bandString, char match)
      {
        for (int index = bandString.Length - 1; index >= 0; index--)
        {
          if (bandString[index].Equals(match)) {
            switch (index) {
              case 0:
                return 90;
              case 1:
                return 60;
              case 2:
                return 30;
              case 3:
                return 10;
              case 4:
                return 5;
              default:
                return 10;
            }
          }
        }
        return 10;
      }

    /// <summary>
    /// Where str1 may have a wild card '?' 
    /// </summary>
    /// <param name="str1"></param>
    /// <param name="str2"></param>
    /// <returns></returns>
    private bool AreEqual(String str1,String str2)
      {
        if(str1.Length!=str2.Length)return false;
        for(int index=0;index<str1.Length;index++)
        {
          char ch1 = str1[index];
          char ch2 = str2[index];
          if(ch1.Equals('?'))continue;
          if(!ch1.Equals(ch2))return false;
        }
        return true;
      }

      private String GetValue(double value)
      {
        if(Math.Abs(value)<.0001)value=0.00;
        if(value==0.00)return "0";
        else if(value>0.00)return "+";
        return "-";
      }

      private double GetKLBandSpreadSlope(BollingerBandElementsByDate bollingerBandElementsByDate, DateTime fromDate, int dayCount)
      {
        List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
        DateGenerator dateGenerator = new DateGenerator();
        List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
        foreach (DateTime historicalDate in historicalDates) {
          if (!bollingerBandElementsByDate.ContainsKey(historicalDate)) {
            continue;
          }
          bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
        }
        List<double> spreadElements = new List<double>();
        foreach (BollingerBandElement bollingerBandElement in bollingerBandElements) {
          spreadElements.Add(bollingerBandElement.K - bollingerBandElement.L);
        }
        double[] elements = spreadElements.ToArray<double>();
        elements = Numerics.Reverse(ref elements);
        double[] logElements = new double[elements.Length];
        for (int index = 0; index < elements.Length; index++) {
          logElements[index] = Math.Log(elements[index]);
        }
        LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
        return leastSquaresResult.Slope;
      }

      private double GetKBandSlope(BollingerBandElementsByDate bollingerBandElementsByDate,DateTime fromDate, int dayCount)
      {
        List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
        DateGenerator dateGenerator = new DateGenerator();
        List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
        foreach(DateTime historicalDate in historicalDates)
        {
          if(!bollingerBandElementsByDate.ContainsKey(historicalDate))
          {
            continue;
          }
          bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
        }
        List<double> bandElements = new List<double>();
        foreach(BollingerBandElement bollingerBandElement in bollingerBandElements)
        {
          bandElements.Add(bollingerBandElement.K);
        }
        double[] elements = bandElements.ToArray<double>();
        elements = Numerics.Reverse(ref elements);
        double[] logElements = new double[elements.Length];
        for(int index=0;index<elements.Length;index++)
        {
          logElements[index] = Math.Log(elements[index]);
        }
        LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
        return leastSquaresResult.Slope;
      }

      private double GetLBandSlope(BollingerBandElementsByDate bollingerBandElementsByDate,DateTime fromDate, int dayCount)
      {
        List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
        DateGenerator dateGenerator = new DateGenerator();
        List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
        foreach(DateTime historicalDate in historicalDates)
        {
          if(!bollingerBandElementsByDate.ContainsKey(historicalDate))
          {
            continue;
          }
          bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
        }
        List<double> bandElements = new List<double>();
        foreach(BollingerBandElement bollingerBandElement in bollingerBandElements)
        {
          bandElements.Add(bollingerBandElement.L);
        }
        double[] elements = bandElements.ToArray<double>();
        elements = Numerics.Reverse(ref elements);
        double[] logElements = new double[elements.Length];
        for(int index=0;index<elements.Length;index++)
        {
          logElements[index] = Math.Log(elements[index]);
        }
        LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
        return leastSquaresResult.Slope;
      }

      public void DisplaySlopes()
      {
        Display($"      90              60             30                 10                 5");
        //Display($"K     {GetValue(bandSlopeK90)}               {GetValue(bandSlopeK60)}              {GetValue(bandSlopeK30)}                  {GetValue(bandSlopeK10)}                  {GetValue(bandSlopeK5)} ");
        //Display($"L     {GetValue(bandSlopeL90)}               {GetValue(bandSlopeL60)}              {GetValue(bandSlopeL30)}                  {GetValue(bandSlopeL10)}                  {GetValue(bandSlopeL5)} ");
        Display($"K-L   {GetValue(bandSlopeSpreadKL90)}               {GetValue(bandSlopeSpreadKL60)}              {GetValue(bandSlopeSpreadKL30)}                  {GetValue(bandSlopeSpreadKL10)}                  {GetValue(bandSlopeSpreadKL5)} ");
      }

      public void DisplaySlopesNumeric()
      {
        Display($"      90              60             30                 10                 5");
        //Display($"K     {Utility.FormatNumber(bandSlopeK90,10)}               {Utility.FormatNumber(bandSlopeK60,10)}              {Utility.FormatNumber(bandSlopeK30,10)}                  {Utility.FormatNumber(bandSlopeK10,10)}                  {Utility.FormatNumber(bandSlopeK5,10)} ");
        //Display($"L     {Utility.FormatNumber(bandSlopeL90,10)}               {Utility.FormatNumber(bandSlopeL60,10)}              {Utility.FormatNumber(bandSlopeL30,10)}                  {Utility.FormatNumber(bandSlopeL10,10)}                  {Utility.FormatNumber(bandSlopeL5,10)} ");
        Display($"L-L     {Utility.FormatNumber(bandSlopeSpreadKL90,10)}               {Utility.FormatNumber(bandSlopeSpreadKL60,10)}              {Utility.FormatNumber(bandSlopeSpreadKL30,10)}                  {Utility.FormatNumber(bandSlopeSpreadKL10,10)}                  {Utility.FormatNumber(bandSlopeSpreadKL5,10)} ");
      }

      private void Display(String message)
      {
        if(Verbose)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,message);
        }
      }
    }
}
