using System;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using TradeBlotter.Command;
using TradeBlotter.Cache;
using TradeBlotter.Model;
using Telerik.Charting;
using Telerik.Windows.Controls.Charting;
using MarketData;
using MarketData.ValueAtRisk;
using MarketData.Utils;
using System.Windows.Controls;
using TradeBlotter.UIUtils;

namespace TradeBlotter.ViewModels
{
  public class CategoricalDataPointWithDesc : CategoricalDataPoint
  {
    public String CustomLabel { get; set; }
  }

  public class RiskProfileViewModel : WorkspaceViewModel
  {
    private PortfolioTrades portfolioTrades = null;
    private List<CategoricalDataPointWithDesc> betaContributionDataPoints = null;
    private List<CategoricalDataPointWithDesc> betaDataPoints = null;
    private PortfolioHoldingsWithBeta portfolioHoldingsWithBeta = null;

    private List<CategoricalDataPointWithDesc> sharpeRatioContributionDataPoints = null;
    private List<CategoricalDataPointWithDesc> sharpeRatioSharpeRatioDataPoints = null;
    private PortfolioHoldingsWithSharpeRatio portfolioHoldingsWithSharpeRatio = null;

// Command Targets
    private RelayCommand resetCommand;
    private RelayCommand proformaAddPositionCommand;
    private RelayCommand chooseDateCommand;
    private RelayCommand chooseDateLostFocusCommand; 

    private String selectedAccount = Constants.CONST_ALL;
    private bool busyIndicator = false;

    private List<DateTime> openTradeDates;
    private List<CalendarDateRange> blackoutDates;
    private DateTime selectedDate;
    private DateTime displayDate;
    private bool isCalendarOpen = false;

    public RiskProfileViewModel()
    {
      base.DisplayName = "Risk Profile";
      BusyIndicator = true;
      portfolioTrades = PortfolioDA.GetTrades();
      openTradeDates = PortfolioTrades.GetOpenTradeDates();
      GetBlackoutDates();
      selectedDate = openTradeDates[0];
      displayDate = selectedDate;
      PropertyChanged += OnViewModelPropertyChanged;
      SelectedAccount = Constants.CONST_ALL;
      BusyIndicator = false;
    }
    private void GetBlackoutDates()
    {
      DateGenerator dateGenerator = new DateGenerator();
      blackoutDates=new List<CalendarDateRange>();
      Dictionary<DateTime,DateTime> openTradeDatesDict = new Dictionary<DateTime, DateTime>();
      List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDatesActual(openTradeDates[0], openTradeDates[openTradeDates.Count - 1]);
      foreach(DateTime openTradeDate in openTradeDates)if(!openTradeDatesDict.ContainsKey(openTradeDate))openTradeDatesDict.Add(openTradeDate,openTradeDate);
      foreach(DateTime historicalDate in historicalDates)if(!openTradeDatesDict.ContainsKey(historicalDate))blackoutDates.Add(new CalendarDateRange(historicalDate,historicalDate));
    }
    public override SaveParameters GetSaveParameters()                               
    {
      return null;
    }
    public override void SetSaveParameters(SaveParameters saveParameters)
    {
    }
    public override String Title
    {
      get
      {
        if (!Constants.CONST_ALL.Equals(selectedAccount)) return base.DisplayName + " - " + selectedAccount;
        return base.DisplayName;
      }
    }
    public override bool CanPersist()
    {
      return false;
    }
    protected override void OnDispose()
    {
    }
    public bool BusyIndicator
    {
      get
      {
        return busyIndicator;
      }
      set
      {
        busyIndicator = value;
        base.OnPropertyChanged("BusyIndicator");
      }
    }
    private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
    {
      if (eventArgs.PropertyName.Equals("SelectedAccount") || eventArgs.PropertyName.Equals("Accounts"))
      {
        BusyIndicator = true;
        Task workerTask = Task.Factory.StartNew(() =>
        {
          portfolioTrades = PortfolioDA.GetOpenTradesAsOf(selectedDate);
          portfolioTrades = portfolioTrades.RemoveClosedTradesWhereClosedOn(selectedDate);
          CreateBetaContribution(portfolioTrades,selectedDate);
          CreateSharpeRatioContribution(portfolioTrades,selectedDate);
        });
        workerTask.ContinueWith((continuation) =>
        {
          BusyIndicator = false;                     
          base.OnPropertyChanged("CartesianChartDataSharpeRatioContribution");
          base.OnPropertyChanged("CartesianChartDataSharpeRatioSharpeRatio");
          base.OnPropertyChanged("CartesianChartDataBetaContribution");
          base.OnPropertyChanged("CartesianChartDataBeta");
          base.OnPropertyChanged("TitleBetaContribution");
          base.OnPropertyChanged("TitleSharpeRationContribution");
          base.OnPropertyChanged("BetaDescription");
        });
      }
      else if (eventArgs.PropertyName.Equals("SelectedDate"))
      {
        BusyIndicator = true;
        Task workerTask = Task.Factory.StartNew(() =>
        {
          portfolioTrades = PortfolioDA.GetOpenTradesAsOf(selectedDate);
          portfolioTrades = portfolioTrades.RemoveClosedTradesWhereClosedOn(selectedDate);
          CreateBetaContribution(portfolioTrades, selectedDate);
          CreateSharpeRatioContribution(portfolioTrades, selectedDate);
        });
        workerTask.ContinueWith((continuation) =>
        {
          BusyIndicator = false;
          base.OnPropertyChanged("CartesianChartDataSharpeRatioContribution");
          base.OnPropertyChanged("CartesianChartDataSharpeRatioSharpeRatio");
          base.OnPropertyChanged("CartesianChartDataBetaContribution");
          base.OnPropertyChanged("CartesianChartDataBeta");
          base.OnPropertyChanged("TitleBetaContribution");
          base.OnPropertyChanged("TitleSharpeRationContribution");
          base.OnPropertyChanged("BetaDescription");
        });
      }
    }
    private void CreateBetaContribution(PortfolioTrades portfolioTrades, DateTime analysisDate)
    {
      PortfolioTrades accountTrades = GetAccountTrades();
      PortfolioHoldings portfolioHoldings = PortfolioHoldings.GetPortfolioHoldings(accountTrades, 5, analysisDate);

      betaContributionDataPoints = new List<CategoricalDataPointWithDesc>();
      betaDataPoints = new List<CategoricalDataPointWithDesc>();

      portfolioHoldingsWithBeta = new PortfolioHoldingsWithBeta(portfolioHoldings, analysisDate);
      portfolioHoldingsWithBeta = new PortfolioHoldingsWithBeta((from PortfolioHoldingWithBeta portfolioHoldingWithBeta in portfolioHoldingsWithBeta select portfolioHoldingWithBeta).OrderByDescending(x => x.BetaContribution).ToList());
      foreach (PortfolioHoldingWithBeta portfolioHoldingWithBeta in portfolioHoldingsWithBeta)
      {
        CategoricalDataPointWithDesc categoricalDataPoint = new CategoricalDataPointWithDesc();
        categoricalDataPoint.Category = portfolioHoldingWithBeta.Symbol; 
        categoricalDataPoint.Value = double.IsNaN(portfolioHoldingWithBeta.BetaContribution)?double.NaN:double.Parse(Utility.FormatNumber(portfolioHoldingWithBeta.BetaContribution*100, 2));
        categoricalDataPoint.CustomLabel = "cR=" + Utility.FormatPercent(portfolioHoldingWithBeta.BetaContribution);
        betaContributionDataPoints.Add(categoricalDataPoint);
      }
      foreach (PortfolioHoldingWithBeta portfolioHoldingWithBeta in portfolioHoldingsWithBeta)
      {
        CategoricalDataPointWithDesc categoricalDataPoint = new CategoricalDataPointWithDesc();
        categoricalDataPoint.Category = portfolioHoldingWithBeta.Symbol;
        categoricalDataPoint.Value = double.IsNaN(portfolioHoldingWithBeta.Beta) ? double.NaN : double.Parse(Utility.FormatNumber(portfolioHoldingWithBeta.Beta * 100.00, 2));
        categoricalDataPoint.CustomLabel = "b="+Utility.FormatNumber(portfolioHoldingWithBeta.Beta,2);
        betaDataPoints.Add(categoricalDataPoint);
      }
    }
    private void CreateSharpeRatioContribution(PortfolioTrades portfolioTrades,DateTime analysisDate)
    {
      PortfolioTrades accountTrades = GetAccountTrades();
      PortfolioHoldings portfolioHoldings = PortfolioHoldings.GetPortfolioHoldings(accountTrades, 5, analysisDate);
      sharpeRatioContributionDataPoints = new List<CategoricalDataPointWithDesc>();
      sharpeRatioSharpeRatioDataPoints = new List<CategoricalDataPointWithDesc>();
      portfolioHoldingsWithSharpeRatio = new PortfolioHoldingsWithSharpeRatio(portfolioHoldings,analysisDate);
      portfolioHoldingsWithSharpeRatio = new PortfolioHoldingsWithSharpeRatio((from PortfolioHoldingWithSharpeRatio portfolioHoldingWithSharpeRatio in portfolioHoldingsWithSharpeRatio select portfolioHoldingWithSharpeRatio).OrderByDescending(x => x.SharpeRatioContribution).ToList());
      foreach (PortfolioHoldingWithSharpeRatio portfolioHoldingWithSharpeRatio in portfolioHoldingsWithSharpeRatio)
      {
        CategoricalDataPointWithDesc categoricalDataPoint = new CategoricalDataPointWithDesc();
        categoricalDataPoint.Category = portfolioHoldingWithSharpeRatio.Symbol; 
        categoricalDataPoint.Value = double.IsNaN(portfolioHoldingWithSharpeRatio.SharpeRatioContribution)?double.NaN:double.Parse(Utility.FormatNumber(portfolioHoldingWithSharpeRatio.SharpeRatioContribution*100, 2));
        categoricalDataPoint.CustomLabel = "cR=" + Utility.FormatPercent(portfolioHoldingWithSharpeRatio.SharpeRatioContribution);
        sharpeRatioContributionDataPoints.Add(categoricalDataPoint);
      }
      foreach (PortfolioHoldingWithSharpeRatio portfolioHoldingWithSharpeRatio in portfolioHoldingsWithSharpeRatio)
      {
        CategoricalDataPointWithDesc categoricalDataPoint = new CategoricalDataPointWithDesc();
        categoricalDataPoint.Category = portfolioHoldingWithSharpeRatio.Symbol;
        categoricalDataPoint.Value = double.IsNaN(portfolioHoldingWithSharpeRatio.SharpeRatio) ? double.NaN : double.Parse(Utility.FormatNumber(portfolioHoldingWithSharpeRatio.SharpeRatio * 100.00, 2));
        categoricalDataPoint.CustomLabel = "sR="+Utility.FormatNumber(portfolioHoldingWithSharpeRatio.SharpeRatio,2);
        sharpeRatioSharpeRatioDataPoints.Add(categoricalDataPoint);
      }
    }
    public PortfolioTrades GetAccountTrades()
    {
      if (Constants.CONST_ALL.Equals(selectedAccount)) return portfolioTrades;
      return portfolioTrades.FilterAccount(selectedAccount);
    }
// ***************************************************************************************************************************************************
// ************************************************************* C H A R T I N G  C O L L E C T I O N S  *********************************************
// ***************************************************************************************************************************************************
    public ObservableCollection<CategoricalDataPoint> CartesianChartDataBetaContribution
    {
      get
      {
        if (null == betaContributionDataPoints) return null;
        return new ObservableCollection<CategoricalDataPoint>(betaContributionDataPoints);
      }
    }
    public ObservableCollection<CategoricalDataPoint> CartesianChartDataBeta
    {
      get
      {
        if (null == betaDataPoints) return null;
        return new ObservableCollection<CategoricalDataPoint>(betaDataPoints);
      }
    }
    public ObservableCollection<CategoricalDataPoint> CartesianChartDataSharpeRatioContribution
    {
      get
      {
        if (null == sharpeRatioContributionDataPoints) return null;
        return new ObservableCollection<CategoricalDataPoint>(sharpeRatioContributionDataPoints);
      }
    }
    public ObservableCollection<CategoricalDataPoint> CartesianChartDataSharpeRatioSharpeRatio
    {
      get
      {
        if (null == sharpeRatioSharpeRatioDataPoints) return null;
        return new ObservableCollection<CategoricalDataPoint>(sharpeRatioSharpeRatioDataPoints);
      }
    }
    public List<String> Accounts
    {
      get
      {
        if (null == portfolioTrades)
        {
          portfolioTrades = PortfolioDA.GetOpenTradesAsOf(selectedDate);
          portfolioTrades = portfolioTrades.RemoveClosedTradesWhereClosedOn(selectedDate);
        }
        List<String> accounts = portfolioTrades.Accounts;
        accounts.Insert(0, Constants.CONST_ALL);
        return accounts;
      }
    }
    public String TitleBetaContribution
    {
      get
      {
        StringBuilder sb = new StringBuilder();
        sb.Append("Beta Contribution - ");
        sb.Append(String.Format("{0}",selectedDate.ToShortDateString())).Append(" ");
        if (Constants.CONST_ALL.Equals(selectedAccount)) sb.Append("All Accounts ");
        else sb.Append("Account ").Append(selectedAccount);
        sb.Append(" (beta=");
        if (null != portfolioHoldingsWithBeta) sb.Append(Utility.FormatNumber(portfolioHoldingsWithBeta.PortfolioBeta));
        sb.Append(")");
        return sb.ToString();
      }
    }
    public String TitleSharpeRationContribution
    {
      get
      {
        StringBuilder sb = new StringBuilder();
        sb.Append("Sharpe Ratio (Contribution to Risk Adjusted Return) - ");
        sb.Append(String.Format("{0}",selectedDate.ToShortDateString())).Append(" ");
        if (Constants.CONST_ALL.Equals(selectedAccount)) sb.Append("All Accounts ");
        else sb.Append("Account ").Append(selectedAccount);
        sb.Append(" (Sharpe Ratio=");
        if (null != portfolioHoldingsWithSharpeRatio) sb.Append(Utility.FormatNumber(portfolioHoldingsWithSharpeRatio.TotalSharpeRatio));
        sb.Append(")");
        return sb.ToString();
      }
    }
    public String BetaDescription
    {
      get
      {
        if (null == portfolioHoldingsWithBeta) return "";
        StringBuilder sb = new StringBuilder();
        sb.Append("This portfolio is expected to rise or fall by a multiple of ").Append(Utility.FormatNumber(portfolioHoldingsWithBeta.PortfolioBeta,3));
        sb.Append(" or ").Append(Utility.FormatCurrency(.01 * portfolioHoldingsWithBeta.PortfolioBeta * portfolioHoldingsWithBeta.Exposure)).Append("");
        sb.Append(" for every 1% of market movement.  ** weight / beta contribution / beta **");
        return sb.ToString();
      }
    }
//*****************************************************************************************************************************************************************
// ******************************************************************* I C O M M A N D   **************************************************************************
//*****************************************************************************************************************************************************************
    public ICommand ResetCommand
    {
      get
      {
        if (null == resetCommand)
        {
          resetCommand = new RelayCommand(param => 
          { 
            selectedDate = displayDate=openTradeDates[0]; 
            base.OnPropertyChanged("SelectedDate"); 
            base.OnPropertyChanged("DisplayDate"); 
          }, param => { return true; });
        }
        return resetCommand;
      }
    }
    public ICommand ChooseDateCommand
    {
      get
      {
        if (null == chooseDateCommand)
        {
          chooseDateCommand = new RelayCommand(param => 
          { 
            isCalendarOpen = !isCalendarOpen; 
            base.OnPropertyChanged("IsCalendarOpen"); 
          }, param => { return true; });
        }
        return chooseDateCommand;
      }
    }
    public ICommand ProformaAddPositionCommand
    {
      get
      {
        if (null == proformaAddPositionCommand)
        {
          proformaAddPositionCommand = new RelayCommand(param =>
          {
            portfolioTrades=ProformaAddPositionDialog.Prompt("Proforma Add Position", SelectedAccount, SelectedDate, portfolioTrades);
            CreateBetaContribution(portfolioTrades, selectedDate);
            CreateSharpeRatioContribution(portfolioTrades, selectedDate);
            base.OnPropertyChanged("CartesianChartDataSharpeRatioContribution");
            base.OnPropertyChanged("CartesianChartDataSharpeRatioSharpeRatio");
            base.OnPropertyChanged("CartesianChartDataBetaContribution");
            base.OnPropertyChanged("CartesianChartDataBeta");
            base.OnPropertyChanged("TitleBetaContribution");
            base.OnPropertyChanged("TitleSharpeRationContribution");
            base.OnPropertyChanged("BetaDescription");
          }, param => { return true; });
        }
        return proformaAddPositionCommand;
      }
    }
//*****************************************************************************************************************************************************************
// ************************************************************************** A C C O U N T   *********************************************************************
//*****************************************************************************************************************************************************************
    public String SelectedAccount
    {
      get { return selectedAccount; }
      set 
      { 
        selectedAccount = value;
        isCalendarOpen = false;
        base.OnPropertyChanged("IsCalendarOpen");
        base.OnPropertyChanged("SelectedAccount"); 
      }
    }
//*****************************************************************************************************************************************************************
// ******************************************************************* C A L E N D A R   M A N A G E M E N T  *****************************************************
//*****************************************************************************************************************************************************************
    public bool IsCalendarOpen
    {
      get { return isCalendarOpen; }
      set { isCalendarOpen = value; base.OnPropertyChanged("IsCalendarOpen"); }
    }
    public DateTime SelectedDate
    {
      get{return selectedDate;}
      set { isCalendarOpen = false; selectedDate = value; displayDate = value; base.OnPropertyChanged("IsCalendarOpen"); base.OnPropertyChanged("DisplayDate"); base.OnPropertyChanged("SelectedDate"); }
    }
    public DateTime DisplayDateStart
    {
      get{return openTradeDates[openTradeDates.Count-1];}
    }
    public DateTime DisplayDateEnd
    {
      get{return openTradeDates[0];}
    }
    public DateTime DisplayDate
    {
      get { return displayDate; }
      set { displayDate = value;base.OnPropertyChanged("DisplayDate");}
    }
    public List<CalendarDateRange> BlackoutDates
    {
      get
      {
        return blackoutDates;
      }
    }
  }
}
