using System;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows;
using System.Linq;
using System.Text;
using MarketData;
using MarketData.Utils;
using MarketData.DataAccess;
using TradeBlotter.DataAccess;
using TradeBlotter.Command;
using TradeBlotter.UIUtils;
using TradeBlotter.Cache;
using System.Windows.Forms;
using MarketData.MarketDataModel;
using TradeBlotter.Model;

namespace TradeBlotter.ViewModels
{
  public delegate void EditHandler(int tradeId);     // so we can post the event back up to the main window
  public delegate void DeleteHandler(int tradeId);   // so we can post the event back up to the main window

  public class BlotterViewModel : WorkspaceViewModel
  {
    private static readonly String LOADING_TRADES = "Loading Trades...";
    private static readonly String REFRESHING_PRICES = "Refreshing Prices...";
    private readonly TradeRepository tradeRepository;
    private PortfolioTrades portfolioTrades;
    double annualYieldAmt=double.NaN;
    double annualYieldPcnt=double.NaN;
    private RelayCommand refreshCommand;
    private RelayCommand deleteCommand;
    private RelayCommand editCommand;
    private EditHandler editHandler;
    private DeleteHandler deleteHandler;
    private TradeViewModel selectedItem = null;
    private bool busyIndicator = false;
    private String busyContent = LOADING_TRADES;
    private bool gridChanged = false;

    public BlotterViewModel(TradeRepository tradeRepository,EditHandler editHandler,DeleteHandler deleteHandler)
    {
      this.editHandler = editHandler;
      this.deleteHandler = deleteHandler;
      base.DisplayName = "TradeView";
      this.tradeRepository = tradeRepository;
      this.CreateAllTrades();
      tradeRepository.TradeAdded += this.OnTradeAddedToRepository;
    }
    public override SaveParameters GetSaveParameters()
    {
      return null;
    }
    public override void SetSaveParameters(SaveParameters saveParameters)
    {
    }
    public override bool CanPersist()
    {
      return false;
    }
    protected override void OnDispose()
    {
    }
    public ObservableCollection<TradeViewModel> AllTrades
    { 
      get; private set; 
    }
    public bool BusyIndicator
    {
      get { return busyIndicator; }
      set
      {
        busyIndicator = value;
        base.OnPropertyChanged("BusyIndicator");
      }
    }
    public String BusyContent
    {
      get { return busyContent; }
      set
      {
        busyContent = value;
        base.OnPropertyChanged("BusyContent");
      }
    }
    public TradeViewModel SelectedItem
    {
      get 
      { 
        return selectedItem; 
      }
      set
      {
        selectedItem = value;
        foreach (TradeViewModel tvm in this.AllTrades)
        {
          tvm.IsSelected = false;
        }
        if (null != selectedItem) selectedItem.IsSelected = true;
      }
    }
    private void CreateAllTrades()
    {
      BusyIndicator = true;
      Task workerTask=Task.Factory.StartNew(() =>
      {
        LoadPortfolioTrades();
        List<BlotterTradeModel> repositoryTrades=tradeRepository.GetTrades(); 
        repositoryTrades=repositoryTrades.OrderByDescending(x=>x.TradeDate).ThenBy(x=>x.TradeId).ToList();
        List<TradeViewModel> all = (from trade in repositoryTrades select new TradeViewModel(trade, tradeRepository)).ToList();
        double totalMarketValue = 0;
        foreach (TradeViewModel tradeViewModel in all)
        {
          if (!tradeViewModel.IsOpen) continue;
          totalMarketValue += Utility.ParseCurrency(tradeViewModel.MarketValue);
        }
        foreach (TradeViewModel tradeViewModel in all)
        {
          tradeViewModel.Weight = Utility.FormatPercent(Utility.ParseCurrency(tradeViewModel.MarketValue) / totalMarketValue);
          tradeViewModel.PropertyChanged += this.OnTradeViewModelPropertyChanged;
        }
        this.AllTrades = new ObservableCollection<TradeViewModel>(all);
        this.AllTrades.CollectionChanged += this.OnCollectionChanged;
      });
      workerTask.ContinueWith((continuation) =>
      {
        BusyIndicator = false;
        base.OnPropertyChanged("AllTrades");
        base.OnPropertyChanged("TotalExposure");
        base.OnPropertyChanged("TotalMarketValue");
        base.OnPropertyChanged("TotalGainLoss");
        base.OnPropertyChanged("TotalReturn");
        base.OnPropertyChanged("TotalGainLossAll");
        base.OnPropertyChanged("TotalReturnAll");
        base.OnPropertyChanged("AnnualDividendYieldAmt");
        base.OnPropertyChanged("AnnualDividendYieldPcnt");
      });
    }
    public void LoadPortfolioTrades()
    {
      portfolioTrades=PortfolioDA.GetOpenTrades();
      double weightAdjustedDividendYield=portfolioTrades.GetWeightAdjustedDividendYield();
      double exposure=portfolioTrades.Exposure();
      annualYieldAmt=weightAdjustedDividendYield*exposure;
      annualYieldPcnt=weightAdjustedDividendYield;
    }
    public String TotalExposure
    {
      get
      {
        if (null == this.AllTrades) return null;
        var openTrades = from trade in this.AllTrades where trade.IsOpen select trade;
        return Utility.FormatCurrency(openTrades.Sum(trade => Utility.ParseCurrency(trade.Exposure)));
      }
    }
    public String TotalMarketValue
    {
      get
      {
        if (null == this.AllTrades) return null;
        var openTrades = from trade in this.AllTrades where trade.IsOpen select trade;
        return Utility.FormatCurrency(openTrades.Sum(trade => Utility.ParseCurrency(trade.MarketValue)));
      }
    }
    public String TotalGainLoss
    {
      get
      {
        if (null == this.AllTrades) return null;
        var openTrades = from trade in this.AllTrades where trade.IsOpen select trade;
        return Utility.FormatCurrency(openTrades.Sum(trade => Utility.ParseCurrency(trade.MarketValue) - Utility.ParseCurrency(trade.Exposure)));
      }
    }
    public String TotalReturn
    {
      get
      {
        if (null == this.AllTrades) return null;
        var openTrades = from trade in this.AllTrades where trade.IsOpen select trade;
        double totalExposure = openTrades.Sum(trade => trade.Price * trade.Shares);
        double totalMarketValue = openTrades.Sum(trade => Utility.ParseCurrency(trade.CurrentPrice) * trade.Shares);
        return Utility.FormatPercent((totalMarketValue-totalExposure)/totalExposure);
      }
    }
    public String TotalGainLossAll
    {
      get
      {
        if (null == this.AllTrades) return null;
        var allTrades = from trade in this.AllTrades select trade;
        double totalExposure = allTrades.Sum(trade => trade.Price * trade.Shares);
        double totalMarketValue = allTrades.Sum(trade => Utility.ParseCurrency((trade.IsOpen ? trade.CurrentPrice : trade.SellPrice)) * trade.Shares);
        return Utility.FormatCurrency(totalMarketValue - totalExposure);
      }
    }
    public String TotalReturnAll
    {
      get
      {
        if (null == this.AllTrades) return null;
        var allTrades = from trade in this.AllTrades select trade;
        double totalExposure = allTrades.Sum(trade => trade.Price * trade.Shares);
        double totalMarketValue = allTrades.Sum(trade => Utility.ParseCurrency((trade.IsOpen?trade.CurrentPrice:trade.SellPrice))* trade.Shares);
        return Utility.FormatPercent((totalMarketValue - totalExposure) / totalExposure);
      }
    }
    public String AnnualDividendYieldPcnt
    {
      get
      {
        return Utility.FormatPercent(annualYieldPcnt);
      }
    }
    public String AnnualDividendYieldAmt
    {
      get
      {
        return Utility.FormatCurrency(annualYieldAmt);
      }
    }
    private void OnTradeAddedToRepository(Object sender, TradeAddedEventArgs e)
    {
      var viewModel = new TradeViewModel(e.NewTrade, tradeRepository);
      this.AllTrades.Add(viewModel);
    }
    private void OnTradeViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
    }
    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      if (e.NewItems != null && e.NewItems.Count != 0)
        foreach (TradeViewModel tradeVM in e.NewItems)
          tradeVM.PropertyChanged += this.OnTradeViewModelPropertyChanged;

      if (e.OldItems != null && e.OldItems.Count != 0)
        foreach (TradeViewModel tradeVM in e.OldItems)
          tradeVM.PropertyChanged -= this.OnTradeViewModelPropertyChanged;
    }
    public String CompanyDescription
    {
      get
      {
        if(null== selectedItem || null==selectedItem.Symbol)return "No row selected.";
        CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(selectedItem.Symbol);
        if(null==companyProfile || null==companyProfile.Description)return "No description found.";
        return companyProfile.Description;
      }
    }
// **************************************************************************************************************************
// ******************************************************** R E F R E S H ***************************************************
// **************************************************************************************************************************
    public bool GridChanged
    {
      get { return gridChanged; }
      set { gridChanged = value; base.OnPropertyChanged("GridChanged"); }
    }
    private void Refresh()
    {
      BusyContent = REFRESHING_PRICES;
      BusyIndicator = true;
      Dispatcher uiDispatcher = Dispatcher.CurrentDispatcher;   // get the UI dispatcher
      Task workerTask = Task.Factory.StartNew(() =>
      {
        foreach (TradeViewModel tvm in this.AllTrades)
        {
          tvm.UseCache=true;   //  switch to cache when refreshing
          tvm.Invalidate();
        }
      });
      workerTask.ContinueWith((continuation) =>
      {
        BusyIndicator = false;
        BusyContent = LOADING_TRADES;
        uiDispatcher.Invoke((MethodInvoker)delegate()
        {
          foreach (TradeViewModel tvm in this.AllTrades)
          {
            tvm.Validate();
          }
        });
        base.OnPropertyChanged("TotalMarketValue");
        base.OnPropertyChanged("TotalGainLoss");
        base.OnPropertyChanged("TotalReturn");
        GridChanged = !GridChanged;
      });
    }
    private bool CanRefresh
    {
      get { return true; }
    }
    public ICommand RefreshCommand
    {
      get
      {
        if (refreshCommand == null)
        {
          refreshCommand = new RelayCommand(param => this.Refresh(), param => this.CanRefresh);
        }
        return refreshCommand;
      }
    }
    // **************************************************************************************************************************
    // ******************************************************** E D I T  ***************************************************
    // **************************************************************************************************************************
    private void Edit()
    {
      TradeViewModel tvmSelected = null;
      foreach (TradeViewModel tvm in this.AllTrades)
      {
        if (tvm.IsSelected) tvmSelected = tvm;
      }
      if (null == tvmSelected || null==editHandler) return;
      editHandler(tvmSelected.TradeId);
    }
    private bool CanEdit
    {
      get 
      {
        return selectedItem == null ? false : true;
      }
    }
    public ICommand EditCommand
    {
      get
      {
        if (editCommand == null)
        {
          editCommand = new RelayCommand(param => this.Edit(), param => this.CanEdit);
        }
        return editCommand;
      }
    }

// **************************************************************************************************************************
// ******************************************************** D E L E T E   ***************************************************
// **************************************************************************************************************************
    private void Delete()
    {
      TradeViewModel tvmToRemove=null;
      foreach (TradeViewModel tvm in this.AllTrades)
      {
        if (tvm.IsSelected) { tvmToRemove = tvm; break; }
      }
      if (null == tvmToRemove) return;
      string messageBoxText = "This operation cannot be undone, confirm delete trade_id("+tvmToRemove.TradeId+")";
      string caption = "eNavigator";
      MessageBoxButton button = MessageBoxButton.YesNoCancel;
      MessageBoxImage icon = MessageBoxImage.Warning;
      if(MessageBoxResult.Yes!=System.Windows.MessageBox.Show(messageBoxText, caption, button, icon))return;
      tradeRepository.Remove(tvmToRemove.TradeId);
      this.AllTrades.Remove(tvmToRemove);
      base.OnPropertyChanged("TotalMarketValue");
      base.OnPropertyChanged("TotalGainLoss");
      base.OnPropertyChanged("TotalReturn");
      if (null != deleteHandler) deleteHandler(tvmToRemove.TradeId);
    }
    private bool CanDelete
    {
      get { return true; }
    }
    public ICommand DeleteCommand
    {
      get
      {
        if (deleteCommand == null)
        {
          deleteCommand = new RelayCommand(param => this.Delete(), param => this.CanDelete);
        }
        return deleteCommand;
      }
    }
  }
}
