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

namespace MarketData.Generator
{                                               
  public class DividendHistoryGenerator
  {
    private DividendHistoryGenerator()
    {
    }
    public static double GetDividendYield(String symbol)
    {
      try
      {
        Price currentPrice=PricingDA.GetPrice(symbol);
        return GetDividendYield(symbol,currentPrice);
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString()));
        return double.NaN;
      }
    }
    public static double GetDividendYield(String symbol,Price currentPrice)
    {
      try
      {
        int currentYear=DateTime.Now.Year;
        int priorYear=currentYear-1;

        DividendHistory dividendHistory=DividendHistoryCache.GetInstance().GetDividendHistory(symbol,new int[]{currentYear, priorYear});
//        DividendHistory dividendHistory=DividendHistoryDA.GetDividendHistory(symbol,new int[] { currentYear,priorYear });

        if(null==currentPrice||double.IsNaN(currentPrice.Close)||!currentPrice.Date.Year.Equals(currentYear)) return double.NaN;
        DividendHistory lastYearsDividendPayments=new DividendHistory((from DividendHistoryItem item in dividendHistory where item.DivExDate.Year==priorYear&&null!=item.CashAmount select item).ToList());
        DividendHistory currentYearsDividendPayments=new DividendHistory((from DividendHistoryItem item in dividendHistory where item.DivExDate.Year==currentYear&&null!=item.CashAmount select item).ToList());
        if(currentYearsDividendPayments.HasConsecutivePayments(3)) return (currentYearsDividendPayments.Average(x => x.CashAmount.Value)*12)/currentPrice.Close;
        if(0==lastYearsDividendPayments.Count&&0==currentYearsDividendPayments.Count) return double.NaN;
        double lastYearsAverageDividendPayment=lastYearsDividendPayments.Count()>1?lastYearsDividendPayments.Average(x => x.CashAmount.Value):double.NaN;

        // calculate last years min and max dividend payments in order to determine if there were special payments
        double lastYearMinDividendPayment=lastYearsDividendPayments.Count>0?lastYearsDividendPayments.Min(x => x.CashAmount.Value):0.00;
        DividendHistory lastYearsSpecialPayments=lastYearsDividendPayments.Count>0?new DividendHistory((from DividendHistoryItem item in lastYearsDividendPayments where item.CashAmount.Value>3*lastYearMinDividendPayment select item).ToList()):new DividendHistory();
        bool lastYearHasSpecialItems=lastYearsSpecialPayments.Count()>0?true:false;

        if(0==currentYearsDividendPayments.Count)   // if no dividends in current year then go back to last year
        {
          if(lastYearHasSpecialItems) lastYearsDividendPayments=new DividendHistory(lastYearsDividendPayments.Where(x => !lastYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
          else if(1==lastYearsDividendPayments.Count) return lastYearsDividendPayments[0].CashAmount.Value/currentPrice.Close;
          return lastYearsDividendPayments.Sum(x => x.CashAmount.Value)/currentPrice.Close;
        }
        double currentYearMaxDividendPayment=currentYearsDividendPayments.Max(x => x.CashAmount.Value);
        double currentYearMinDividendPayment=currentYearsDividendPayments.Min(x => x.CashAmount.Value);
        DividendHistory currentYearsSpecialPayments=new DividendHistory((from DividendHistoryItem item in currentYearsDividendPayments where item.CashAmount.Value>3*currentYearMinDividendPayment select item).ToList());
        bool currentYearhasSpecialItems=currentYearsSpecialPayments.Count()>0?true:false;
        double currentYearsAverageDividendPayment=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
        if(1==lastYearsDividendPayments.Count&&1==currentYearsDividendPayments.Count) return currentYearsAverageDividendPayment/currentPrice.Close;
        if(0==lastYearsDividendPayments.Count) // there was no dividend payment last year.  check if this years is a special dividend (>25%)
        {
          double dividendPayment=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
          double dividendYield=dividendPayment/currentPrice.Close;
          if(dividendYield>=.25) return dividendYield;
        }
        if(4==lastYearsDividendPayments.Count)
        {
          if(currentYearhasSpecialItems)  // exclude special payments from consideration
          {
            currentYearsDividendPayments=new DividendHistory(currentYearsDividendPayments.Where(x => !currentYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
            double dividendYield=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
            dividendYield*=4.00;
            dividendYield/=currentPrice.Close;
            return dividendYield;
          }
          if(1==currentYearsDividendPayments.Count&&lastYearHasSpecialItems)   // if only one payment this year and last year has a special payment then use last year minus special payment
          {
            lastYearsDividendPayments=new DividendHistory(lastYearsDividendPayments.Where(x => !lastYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
            double dividendYield=lastYearsDividendPayments.Average(x => x.CashAmount.Value);
            dividendYield*=4.00;
            dividendYield/=currentPrice.Close;
            return dividendYield;
          }
          return (currentYearsAverageDividendPayment*4.00)/currentPrice.Close;
        }
        if(3==currentYearsDividendPayments.Count)
        {
          if(currentYearhasSpecialItems)  // exclude special payments from consideration
          {
            currentYearsDividendPayments=new DividendHistory(currentYearsDividendPayments.Where(x => !currentYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
            double dividendYield=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
            dividendYield*=4.00;
            dividendYield/=currentPrice.Close;
            return dividendYield;
          }
          else return (currentYearsAverageDividendPayment*4.00)/currentPrice.Close;
        }
        if(2==lastYearsDividendPayments.Count) return Math.Min(lastYearsDividendPayments.Sum(x => x.CashAmount.Value),currentYearsDividendPayments.Sum(x => x.CashAmount.Value))/currentPrice.Close;
        return (currentYearsDividendPayments.Average(x => x.CashAmount.Value)*4.00)/currentPrice.Close;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString()));
        return double.NaN;
      }
    }
  }
}
