using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;

namespace MarketData.Generator
{
  public class RSIGenerator
  {
    private RSIGenerator()
    {
    }
    public static RSICollection GenerateRSI(String symbol,int priceCount,int rsiDayCount=14)
    {
      if(priceCount<rsiDayCount)priceCount=(rsiDayCount*2)+1;
      Prices prices=PricingDA.GetPrices(symbol,priceCount);
      if(null==prices||priceCount!=prices.Count)return null;
      RSICollection rsiCollection=new RSICollection();
      for(int index=prices.Count-1;index>=0;index--)
      {
        Price price=prices[index];
        RSIElement rsiElement=new RSIElement();
        rsiElement.Symbol=price.Symbol;
        rsiElement.RSIDays=rsiDayCount;
        rsiElement.Date=price.Date;
        rsiElement.Close=price.Close;
        if(index==prices.Count-1)continue;
        rsiElement.Change=price.Close-prices[index+1].Close;
        if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;}
        else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;}
        else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;}
        rsiCollection.Add(rsiElement);
      }
      RSICollection topCollection=rsiCollection.Top(rsiDayCount,1);
      rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain();
      rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss();
      if(0.00==rsiCollection[rsiDayCount].AverageLoss)
      {
        rsiCollection[rsiDayCount].RS=0.00;
        rsiCollection[rsiDayCount].RSI=100.00;
      }
      else
      {
        rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss; 
        rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS));
      }
      for(int index=rsiDayCount+1;index<rsiCollection.Count;index++)
      {
        rsiCollection[index].AverageGain=(rsiCollection[index-1].AverageGain*(rsiDayCount-1)+rsiCollection[index].Gain)/(double)rsiDayCount;
        rsiCollection[index].AverageLoss=(rsiCollection[index-1].AverageLoss*(rsiDayCount-1)+rsiCollection[index].Loss)/(double)rsiDayCount;
        if(0.00==rsiCollection[index].AverageLoss)
        {
          rsiCollection[index].RS=0.00;
          rsiCollection[index].RSI=100.00;
        }
        else
        {
          rsiCollection[index].RS=rsiCollection[index].AverageGain/rsiCollection[index].AverageLoss; 
          rsiCollection[index].RSI=100.00-(100.00/(1.00+rsiCollection[index].RS));
        }
      }
      rsiCollection=new RSICollection(rsiCollection.Skip(rsiDayCount).ToList());
      return rsiCollection;
    }
    
    public static RSICollection GenerateRSI(String symbol,DateTime analysisDate,int priceCount,int rsiDayCount=14)
    {
      if(priceCount<rsiDayCount)priceCount=(rsiDayCount*2)+1;
      Prices prices=PricingDA.GetPrices(symbol,analysisDate,priceCount);
      if(null==prices||priceCount!=prices.Count)return null;
      return GenerateRSI(prices, rsiDayCount);
    }

    public static RSICollection GenerateRSI(Prices prices,int rsiDayCount=14)
    {
      if(null==prices || prices.Count<(rsiDayCount*2))return null;
      RSICollection rsiCollection=new RSICollection();
      for(int index=prices.Count-1;index>=0;index--)
      {
        Price price=prices[index];
        RSIElement rsiElement=new RSIElement();
        rsiElement.Symbol=price.Symbol;
        rsiElement.RSIDays=rsiDayCount;
        rsiElement.Date=price.Date;
        rsiElement.Close=price.Close;
        if(index==prices.Count-1)continue;
        rsiElement.Change=price.Close-prices[index+1].Close;
        if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;}
        else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;}
        else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;}
        rsiCollection.Add(rsiElement);
      }
      RSICollection topCollection=rsiCollection.Top(rsiDayCount,1);
      rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain();
      rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss();
      if(0.00==rsiCollection[rsiDayCount].AverageLoss)
      {
        rsiCollection[rsiDayCount].RS=0.00;
        rsiCollection[rsiDayCount].RSI=100.00;
      }
      else
      {
        rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss; 
        rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS));
      }
      for(int index=rsiDayCount+1;index<rsiCollection.Count;index++)
      {
        rsiCollection[index].AverageGain=(rsiCollection[index-1].AverageGain*(rsiDayCount-1)+rsiCollection[index].Gain)/(double)rsiDayCount;
        rsiCollection[index].AverageLoss=(rsiCollection[index-1].AverageLoss*(rsiDayCount-1)+rsiCollection[index].Loss)/(double)rsiDayCount;
        if(0.00==rsiCollection[index].AverageLoss)
        {
          rsiCollection[index].RS=0.00;
          rsiCollection[index].RSI=100.00;
        }
        else
        {
          rsiCollection[index].RS=rsiCollection[index].AverageGain/rsiCollection[index].AverageLoss; 
          rsiCollection[index].RSI=100.00-(100.00/(1.00+rsiCollection[index].RS));
        }
      }
      rsiCollection=new RSICollection(rsiCollection.Skip(rsiDayCount).ToList());
      return rsiCollection;
    }
  }
}
