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
{
  public class GainLossGeneratorCum : ITotalGainLossGenerator
  {
    private DateGenerator dateGenerator=new DateGenerator();

    public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
    {
      DateGenerator dateGenerator=new DateGenerator();
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
      LocalPriceCache.GetInstance().Add(portfolioTrades);
      try
      {
        if(!ValidatePortfolioTrades(portfolioTrades))return null;
        DateTime minDate=portfolioTrades.GetMinTradeDate();
        DateTime maxDate=PricingDA.GetLatestDate();
        if(null!=maxDateRef) maxDate=maxDateRef.Value;
        double prevGainLoss=double.NaN;
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);

        foreach(DateTime currentDate in historicalDates)
        {
          PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
          PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
          if(0==openPositions.Count&&0==closedPositions.Count) continue;

          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;

          if(!dateGenerator.IsMarketOpen(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();

          foreach(PortfolioTrade openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.Price;
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
              continue;
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(PortfolioTrade closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }
        performanceSeries.CalculatePerformance();
        double totalGainLoss=double.NaN;
        foreach(ModelPerformanceItem performanceItem in performanceSeries)
        {
          if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
          else totalGainLoss+=performanceItem.GainLossDOD;
          TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
            performanceItem.Date,
            totalGainLoss,
            performanceItem.CumProdMinusOne*100.00,
            performanceItem.Exposure,
            performanceItem.MarketValue);
          gainLossList.Add(totalGainLossItem);
        }
        TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
        totalGainLossCollection.Sort();
        return totalGainLossCollection;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
        return new TotalGainLossCollection(new List<TotalGainLossItem>());
      }
    }
    public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
    {
      DateGenerator dateGenerator=new DateGenerator();
      ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
      List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
      LocalPriceCache.GetInstance().Add(portfolioTrades);
      try
      {
        if(!ValidatePortfolioTrades(portfolioTrades)) return null;
        DateTime minDate=portfolioTrades.Min(x => x.TradeDate);
        DateTime maxDate=PricingDA.GetLatestDate();
        double prevGainLoss=double.NaN;
//        LocalPriceCache.GetInstance().RemoveDate(maxDate);
        List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);

        foreach(DateTime currentDate in historicalDates)
        {
          PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
          PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
          if(0==openPositions.Count&&0==closedPositions.Count) continue;

          double gainLoss=0.00;
          double gainLossClosedPositions=0.00;
          double exposure=0.00;
          double marketValue=0.00;
          if(!dateGenerator.IsMarketOpen(currentDate)) continue;
          ModelPerformanceItem performanceItem=new ModelPerformanceItem();

          foreach(PortfolioTrade openPosition in openPositions)
          {
            exposure+=openPosition.Shares*openPosition.Price;
            //if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
            //{
            //  Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
            //  LocalPriceCache.GetInstance().Add(prices);
            //}
            Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
            if(null==price)
            {
              MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
            }
            gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
            marketValue+=(price.Close*openPosition.Shares);
          }
          foreach(PortfolioTrade closedPosition in closedPositions)
          {
            double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
            gainLossClosedPositions+=gainLossPosition;
          }
          double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(currentDate);
          marketValue+=dividendPaymentsToDate;
          gainLoss+=dividendPaymentsToDate;
          performanceItem.Date=currentDate;
          performanceItem.Exposure=exposure;
          performanceItem.MarketValue=marketValue;
          performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
          performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
          performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
          performanceSeries.Add(performanceItem);
          prevGainLoss=gainLoss;
        }
        performanceSeries.CalculatePerformance();
        double totalGainLoss=double.NaN;
        foreach(ModelPerformanceItem performanceItem in performanceSeries)
        {
          double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(performanceItem.Date);
          if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
          else totalGainLoss+=performanceItem.GainLossDOD;
          TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
            performanceItem.Date,
            totalGainLoss,
            performanceItem.CumProdMinusOne*100.00,
            performanceItem.Exposure,
            performanceItem.MarketValue,
            dividendPaymentsToDate);
          gainLossList.Add(totalGainLossItem);
        }
        TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
        totalGainLossCollection.Sort();
        return totalGainLossCollection;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
        return new TotalGainLossCollection(new List<TotalGainLossItem>());
      }
    }
    public bool ValidatePortfolioTrades(PortfolioTrades portfolioTrades)
    {
      foreach(PortfolioTrade portfolioTrade in portfolioTrades)
      {
        if(!dateGenerator.IsMarketOpen(portfolioTrade.TradeDate))return false;
        if(!dateGenerator.IsMarketOpen(portfolioTrade.SellDate)) return false;
      }
      return true;
    }
  }
}
