using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Text;
using MarketData.MarketDataModel;

namespace MarketData.Generator
{
  public class SignalGenerator
	{
		private SignalGenerator()
		{
		}
    public static Signals GenerateSignals(DMAPrices shortMA, DMAPrices longMA)
    {
      try
      {
        Signals signals = new Signals();
        Align(shortMA, longMA);

        for (int index = shortMA.Count - 1; index >= 0; index--)
        {
          Signal currSignal = new Signal();
          Signal prevSignal = null;
          DMAPrice prevShortData = null;
          DMAPrice currShortData = shortMA[index];
          DMAPrice prevLongData = null;
          DMAPrice currLongData = longMA[index];

          currSignal.Ticker = currShortData.Symbol;
          currSignal.SignalDate = currShortData.Date;
          if (index == shortMA.Count - 1)
          {
            if (currShortData.AVGPrice > currLongData.AVGPrice)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
              currSignal.Reason = "Crossover.";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongSell;
              currSignal.Reason = "Crossover.";
            }
            signals.Add(currSignal);
            continue;
          }
          prevSignal = (Signal)signals[signals.Count - 1];
          prevShortData = shortMA[index + 1];
          prevLongData = longMA[index + 1];
          if (currShortData.AVGPrice > currLongData.AVGPrice)
          {
            if (prevShortData.AVGPrice < prevLongData.AVGPrice)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
              currSignal.Reason = "Crossover.";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
              currSignal.Reason = "Existing Crossover maintained.";
            }
          }
          else
          {
            if (prevShortData.AVGPrice > prevLongData.AVGPrice)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongSell;
              currSignal.Reason = "Crossover.";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakSell;
              currSignal.Reason = "Existing Crossover maintained.";
            }
          }
          signals.Add(currSignal);
        }
        return signals;
      }
      catch (Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception);
        return null;
      }
    }
    public static Signals GenerateSignals(Stochastics stochastics)
    {
      try
      {
        Signals signals = new Signals();
        StochasticElement prevStochastic = null;
        Signal prevSignal = null;
        bool crossover = false;

        for (int index = stochastics.Count - 1; index >= 0; index--)
        {
          Signal currSignal = new Signal();
          StochasticElement currStochastic = stochastics[index];
          currSignal.Ticker = currStochastic.Symbol;
          currSignal.SignalDate = currStochastic.Date;
          // identify the crossover
          if (currStochastic.PK > currStochastic.PD && (null != prevStochastic && prevStochastic.PK <= prevStochastic.PD))
          {
            currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
            currSignal.Reason = "currStochastic.PK>currStochastic.PD";
            crossover = true;
          }
          else if (currStochastic.PK <= currStochastic.PD && (null != prevStochastic && prevStochastic.PK > prevStochastic.PD))
          {
            currSignal.SignalIndicator = Signal.Indicator.StrongSell;
            currSignal.Reason = "currStochastic.PK <= currStochastic.PD";
            crossover = true;
          }
          else if (currStochastic.PKInRange(20, 50))
          {
            crossover = false;
            if (null != prevStochastic && prevStochastic.PK < 20)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
              currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PK < 20 ";
            }
            else if (null != prevStochastic && prevStochastic.PK > 50)
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakSell;
              currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PK > 50";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
              currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PKInRange(20,50)";
            }
          }
          else if (currStochastic.PKInRange(51, 79))
          {
            crossover = false;
            if (null != prevStochastic && prevStochastic.PK < 51)
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
              currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PK < 51";
            }
            else if (null != prevStochastic && prevStochastic.PK > 79)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongSell;
              currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PK > 79";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
              currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PKInRange(51,79)";
            }
          }
          else if (currStochastic.PKInRange(80, 100))
          {
            crossover = false;
            if (null != prevStochastic && prevStochastic.PK < 80)
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
              currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK < 80";
            }
            else if (null != prevStochastic && prevStochastic.PK > 90)
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongSell;
              currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK > 90";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
              currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK >80,prevStochastic.PK<90";
            }
          }
          else
          {
            if (crossover)
            {
              currSignal.SignalIndicator = prevSignal.SignalIndicator;
              currSignal.Reason = "currStochastic<20, previous crossover holds.";
            }
            else
            {
              currSignal.SignalIndicator = Signal.Indicator.StrongSell;
              currSignal.Reason = "currStochastic<20, no crossover identified";
            }
          }
          signals.Add(currSignal);
          prevStochastic = currStochastic;
          prevSignal = (Signal)signals[signals.Count - 1];
        }
        return signals;
      }
      catch (Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception);
        return null;
      }
    }
    public static Signals GenerateSignals(MACDSignals macdSignals)
    {
      try
      {
        Signals signals = new Signals();
        SignHolder signMACD = new SignHolder();
        SignHolder signSignal = new SignHolder();

        for (int index = macdSignals.Count - 1; index >= 0; index--)
        {
          Signal currSignal = new Signal();
          Signal prevSignal = null;
          MACDSignal prevSecurityData = null;
          MACDSignal currSecurityData = macdSignals[index];
          currSignal.Ticker = currSecurityData.Symbol;
          currSignal.SignalDate = currSecurityData.Date;
          if (index == macdSignals.Count - 1)
          {
            signMACD.SetValue(currSecurityData.MACD);
            signSignal.SetValue(currSecurityData.Signal);
            if (currSecurityData.MACD > currSecurityData.Signal)
            {
              if (currSecurityData.MACD < 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "MACD is above the signal line and the MACD is less than zero.";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
                currSignal.Reason = "MACD is above the signal line and the MACD is greater than or equal to zero.";
              }
            }
            else
            {
              if (currSecurityData.MACD > 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "MACD is less than or equal to the signal line and MACD is above zero.";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakSell;
                currSignal.Reason = "MACD is less than or equal to the signal line and MACD is less than or equal to zero";
              }
            }
            signals.Add(currSignal);
            continue;
          }
          prevSignal = (Signal)signals[signals.Count - 1];
          prevSecurityData = macdSignals[index + 1];
          signMACD.SetValue(currSecurityData.MACD);
          signSignal.SetValue(currSecurityData.Signal);
          //MACD is above the signal line
          if (currSecurityData.MACD > currSecurityData.Signal)
          {
            // previous MACD is above signal line
            if (prevSignal.IsBuy())
            {
              // the histogram is widening so keep it a strong buy
              if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram) && currSecurityData.MACD < 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "The current MACD is above the signal and the histogram is widening and MACD is below zero";
              }
              else if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram))
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "The current MACD is above the signal and the histogram is widening.";
              }
              else if (currSecurityData.MACD < 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "The current MACD is above the signal and less than zero.";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
                currSignal.Reason = "The current MACD is above the signal line but so was the previous MACD.";
              }
            }
            else
            {
              if (currSecurityData.MACD > 0 && currSecurityData.Signal < 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "The current MACD is above the signal, MACD is above zero, signal is below zero.";
              }
              else if (currSecurityData.MACD > 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
                currSignal.Reason = "The current MACD is above the signal but the MACD is above zero.";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
                currSignal.Reason = "The current MACD is above the signal and the MACD is less than zero";
              }
            }
          }
          //MACD is below the signal line
          else
          {
            // check previous MACD is below the signal line
            // The MACD line is above the signal line but the MACD is below zero so this is considered a weaker buy signal
            if (prevSignal.IsSell())
            {
              if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram) && currSecurityData.MACD > 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "The current MACD is below or equal to the signal, the histogram is widening, and MACD is above zero";
              }
              else if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram))
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "The current MACD is below or equal to the signal, the histogram is widening.";
              }
              else if (currSecurityData.MACD > 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "The current MACD is below or equal to the signal line and MACD is above zero";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakSell;
                currSignal.Reason = "The current MACD is below or equal to the signal line.";
              }
            }
            else
            {
              if (currSecurityData.MACD < 0 && currSecurityData.Signal > 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "The current MACD is below or equal to the signal line, MACD is below zero, signal is above zero";
              }
              else if (currSecurityData.MACD < 0)
              {
                currSignal.SignalIndicator = Signal.Indicator.WeakSell;
                currSignal.Reason = "The current MACD is below or equal to the signal line and MACD is below zero.";
              }
              else
              {
                currSignal.SignalIndicator = Signal.Indicator.StrongSell;
                currSignal.Reason = "The current MACD is below or equal to the signal and MACD is greater than or equal to zero.";
              }
            }
          }
          signals.Add(currSignal);
        }
        signals.Sort(new SignalComparatorByDateDescending());     // This was added to get the most recent dated items into the lowest position of the returned array. Users of this function were performing TAKE() on the array to get the topmost nth items and winding up with the bottommost (furthest dated) items.
        return signals;
      }
      catch (Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception);
        return null;
      }
    }
    private static void Align(DMAPrices shortMA, DMAPrices longMA)
    {
      Dictionary<DateTime, DMAPrice> shortMADates = new Dictionary<DateTime, DMAPrice>();
      Dictionary<DateTime, DMAPrice> longMADates = new Dictionary<DateTime, DMAPrice>();
      List<DateTime> shortMAPriceToRemove = new List<DateTime>();
      List<DateTime> longMAPriceToRemove = new List<DateTime>();

      for (int index = 0; index < shortMA.Count; index++)
      {
        DMAPrice dmaPrice = shortMA[index];
        if (!shortMADates.ContainsKey(dmaPrice.Date)) shortMADates.Add(dmaPrice.Date, dmaPrice);
      }
      for (int index = 0; index < longMA.Count; index++)
      {
        DMAPrice dmaPrice = longMA[index];
        if (!longMADates.ContainsKey(dmaPrice.Date)) longMADates.Add(dmaPrice.Date, dmaPrice);
      }
      for (int index = 0; index < shortMA.Count; index++)
      {
        DMAPrice dmaPrice = shortMA[index];
        if (!longMADates.ContainsKey(dmaPrice.Date)) shortMAPriceToRemove.Add(dmaPrice.Date);
      }
      for (int index = 0; index < longMA.Count; index++)
      {
        DMAPrice dmaPrice = longMA[index];
        if (!shortMADates.ContainsKey(dmaPrice.Date)) longMAPriceToRemove.Add(dmaPrice.Date);
      }
      for (int index = 0; index < shortMAPriceToRemove.Count; index++)
      {
        DateTime dateToRemove = shortMAPriceToRemove[index];
        DMAPrice dmaPrice = shortMADates[dateToRemove];
        shortMA.Remove(dmaPrice);
      }
      for (int index = 0; index < longMAPriceToRemove.Count; index++)
      {
        DateTime dateToRemove = longMAPriceToRemove[index];
        DMAPrice dmaPrice = longMADates[dateToRemove];
        longMA.Remove(dmaPrice);
      }
    }
    private static void PrintSignals(ArrayList signals)
    {
      for(int index=0;index<signals.Count;index++)
      {
        Signal signal=(Signal)signals[index];
        MDTrace.WriteLine(LogLevel.DEBUG,signal.ToString());
      }
    }
  }
}
