using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;

namespace MarketData.Generator.GainLoss
{
// ***************************************************************** G A I N  L O S S  G E N E R A T O R  **********************************************************
  public class GainLossGenerator : ITotalGainLossGenerator
  {
    public GainLossGenerator()
    {
    }
// *****************************************************************************************************************************************************************
// ************************************************ G E N E R A T E  T O T A L   G A I N  L O S S  /  T O T A L  G A I N  L O S S  P E R C E N T *******************
// *****************************************************************************************************************************************************************
    public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
    {
      if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
      LocalPriceCache.GetInstance().Add(portfolioTrades);
      DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
      DateTime maxDate=LocalPriceCache.GetInstance().GetLatestDate();
      if(null!=maxDateRef)maxDate=maxDateRef.Value;
      Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
      DateGenerator dateGenerator = new DateGenerator();
      List<DateTime> holdingDates = dateGenerator.GenerateHistoricalDates(maxDate, minTradeDate);
      for (int index = holdingDates.Count - 1; index >= 0; index--)
      {
        DateTime holdingDate = holdingDates[index];
        double totalGainLoss = 0.00;;
        double totalExposure = 0.00;
        double totalCostBasis=0.00;
        double totalMarketValue=0.00;
        PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(holdingDate);
        if (null == tradesOnOrBefore || 0 == tradesOnOrBefore.Count)
        {
          gainLossCollection.Add(holdingDate, new TotalGainLossItem(holdingDate, 0,0,0,0));
          continue;
        }
        foreach (PortfolioTrade portfolioTrade in tradesOnOrBefore)
        {
          double? gainLoss = null;
          double? exposure = null;
          double? costBasis=null;
          double? marketValue=null;
          gainLoss=GainLossHelper.GetTotalGainLoss(holdingDate,portfolioTrade);
          costBasis=GainLossHelper.GetTotalCostBasis(holdingDate,portfolioTrade);
          marketValue=GainLossHelper.GetTotalMarketValue(holdingDate,portfolioTrade);
          exposure=GainLossHelper.GetTotalExposure(holdingDate,portfolioTrade);
          if (null == gainLoss || null==costBasis || null==marketValue) continue;
          if(null!=gainLoss) totalGainLoss += gainLoss.Value;
          if (null != exposure) totalExposure += exposure.Value;
          if(null!=costBasis)totalCostBasis+=costBasis.Value;
          if(null!=marketValue)totalMarketValue+=marketValue.Value;
        }
        TotalGainLossItem gainLossElement = null;
        double totalGainLossPercent=0;
        if(0!=totalCostBasis)totalGainLossPercent=((totalMarketValue-totalCostBasis)/totalCostBasis)*100.00;
        gainLossElement=new TotalGainLossItem(holdingDate,totalGainLoss,totalGainLossPercent,totalExposure,totalMarketValue);
        gainLossCollection.Add(holdingDate, gainLossElement);
      }
      TotalGainLossCollection gainLossList = new TotalGainLossCollection(gainLossCollection.Values);  
      gainLossList.Sort();
      return gainLossList;
    }
// *****************************************************************************************************************************************************************
// ************************************************ G E N E R A T E  T O T A L   G A I N  L O S S  W I T H  D I V I D E N D S  /  T O T A L  G A I N  L O S S  P E R C E N T *******************
// *****************************************************************************************************************************************************************
    public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
    {
      if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
      LocalPriceCache.GetInstance().Add(portfolioTrades);
      DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
      DateTime maxDate=LocalPriceCache.GetInstance().GetLatestDate();
      if(null!=maxDateRef)maxDate=maxDateRef.Value;
      Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
      DateGenerator dateGenerator = new DateGenerator();
      List<DateTime> holdingDates = dateGenerator.GenerateHistoricalDates(maxDate, minTradeDate);
      for (int index = holdingDates.Count - 1; index >= 0; index--)
      {
        DateTime holdingDate = holdingDates[index];
        double totalGainLoss = 0.00;;
        double totalExposure = 0.00;
        double totalCostBasis=0.00;
        double totalMarketValue=0.00;
        PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(holdingDate);
        if (null == tradesOnOrBefore || 0 == tradesOnOrBefore.Count)
        {
          gainLossCollection.Add(holdingDate, new TotalGainLossItem(holdingDate, 0,0,0,0));
          continue;
        }
        foreach (PortfolioTrade portfolioTrade in tradesOnOrBefore)
        {
          double? gainLoss = null;
          double? exposure = null;
          double? costBasis=null;
          double? marketValue=null;
          gainLoss=GainLossHelper.GetTotalGainLoss(holdingDate,portfolioTrade);
          costBasis=GainLossHelper.GetTotalCostBasis(holdingDate,portfolioTrade);
          marketValue=GainLossHelper.GetTotalMarketValue(holdingDate,portfolioTrade);
          exposure=GainLossHelper.GetTotalExposure(holdingDate,portfolioTrade);
          if (null == gainLoss || null==costBasis || null==marketValue) continue;
          if(null!=gainLoss) totalGainLoss += gainLoss.Value;
          if (null != exposure) totalExposure += exposure.Value;
          if(null!=costBasis)totalCostBasis+=costBasis.Value;
          if(null!=marketValue)totalMarketValue+=marketValue.Value;
        }
        TotalGainLossItem gainLossElement = null;
        double totalGainLossPercent=0;
        double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(holdingDate);
        totalMarketValue+=dividendPaymentsToDate;
        totalGainLoss+=dividendPaymentsToDate;
        if(0!=totalCostBasis)totalGainLossPercent=((totalMarketValue-totalCostBasis)/totalCostBasis)*100.00;
        gainLossElement=new TotalGainLossItem(holdingDate,totalGainLoss,totalGainLossPercent,totalExposure,totalMarketValue,dividendPaymentsToDate);
        gainLossCollection.Add(holdingDate, gainLossElement);
      }
      TotalGainLossCollection gainLossList = new TotalGainLossCollection(gainLossCollection.Values);  
      gainLossList.Sort();
      return gainLossList;
    }
  }
}
