using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using MarketData;
using MarketData.Numerical;
using MarketData.Utils;
using MarketData.MarketDataModel;
using MarketData.Generator;
using MarketData.DataAccess;
using TradeBlotter.DataAccess;
using TradeBlotter.Command;
using TradeBlotter.Model;
using TradeBlotter.Cache;
using Microsoft.Research.DynamicDataDisplay.DataSources;

namespace TradeBlotter.ViewModels
{
  public class OptionsWorksheetViewModel : WorkspaceViewModel
  {
    private const int PRICE_FETCH_INTERVAL_MINS = 2;
    private RelayCommand displayBollingerBandCommand = null;
    private RelayCommand displayMovingAverageCommand = null;
    private Option selectedOption = null;
    private OptionsParams optionsParams = new OptionsParams() { Cashdown = 10000 };
    private String companyName;
    private Price underlierPrice;    // this is the price as of the date this record was created.
    private Price currentPrice;   // this is the most recent price in the database
    private ObservableCollection<EquityPriceShock> equityPriceShocksCollection = null;
    private bool busyIndicator = false;
    private DispatcherTimer priceTimer = null;
    private Prices prices = null;
    private double transactionGL;
    private bool useRealPrice = false;

    public OptionsWorksheetViewModel()
    {
      priceTimer = new DispatcherTimer();
      priceTimer.Tick += new EventHandler(TimerHandler);
      priceTimer.Interval = new TimeSpan(0, PRICE_FETCH_INTERVAL_MINS, 0);
      base.DisplayName = "Options Worksheet";
      PropertyChanged += OnViewModelPropertyChanged;
      base.OnPropertyChanged("Title");
    }
    protected override void OnDispose()
    {
      if (null == priceTimer) return;
      priceTimer.Stop();
    }
    private void TimerHandler(Object sender, EventArgs eventArgs)
    {
      if (null == underlierPrice) return;
      Task workerTask = Task.Factory.StartNew(() =>
      {
        currentPrice = PriceCache.GetInstance().GetLatestPrice(underlierPrice.Symbol);
        if (null == currentPrice || null==prices) return;
        if (prices[0].Date.Date.Equals(currentPrice.Date.Date))
        {
          prices[0] = currentPrice;
        }
        else prices.Insert(0, currentPrice);
      });
      workerTask.ContinueWith((continuation) =>
      {
        base.OnPropertyChanged("CurrentPrice");
        base.OnPropertyChanged("CurrentPriceDate");
        base.OnPropertyChanged("Close");
        base.OnPropertyChanged("TransactionGL");
      });
    }
    // ******************************************************************************************** P E R S I S T E N C E ********************************************************************************************
    public override bool CanPersist()
    {
      return true;
    }
    public override SaveParameters GetSaveParameters()
    {
      SaveParameters saveParams = new SaveParameters();

      XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
      xmlWriterSettings.Indent = false;
      xmlWriterSettings.NewLineHandling = NewLineHandling.None;

      XmlSerializer optionSerializer = new XmlSerializer(selectedOption.GetType());
      StringBuilder optionString=new StringBuilder();
      XmlWriter optionWriter = XmlWriter.Create(optionString, xmlWriterSettings);
      optionSerializer.Serialize(optionWriter, selectedOption);

      XmlSerializer underlierPriceSerializer = new XmlSerializer(typeof(Price));
      StringBuilder underlierPriceString = new StringBuilder();
      XmlWriter underlierPriceWriter = XmlWriter.Create(underlierPriceString, xmlWriterSettings);
      underlierPriceSerializer.Serialize(underlierPriceWriter, underlierPrice);

      saveParams.Add(new KeyValuePair<String, String>("Type", "TradeBlotter.ViewModels.OptionsWorksheetViewModel"));
      saveParams.Add(new KeyValuePair<String, String>("Option", optionString.ToString()));
      saveParams.Add(new KeyValuePair<String, String>("CompanyName", companyName));
      saveParams.Add(new KeyValuePair<String, String>("Price", underlierPriceString.ToString()));
      return saveParams;
    }
    public override void SetSaveParameters(SaveParameters saveParameters)
    {
      try
      {
        String strSelectedOption = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("Option") select item).FirstOrDefault().Value;
        String strLatestPrice = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("Price") select item).FirstOrDefault().Value;
        companyName = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("CompanyName") select item).FirstOrDefault().Value;

        XmlSerializer optionSerializer = new XmlSerializer(typeof(Option));
        StringReader stringReader = new StringReader(strSelectedOption);
        selectedOption = (Option)optionSerializer.Deserialize(stringReader);

        XmlSerializer priceSerializer = new XmlSerializer(typeof(Price));
        StringReader priceReader = new StringReader(strLatestPrice);
        underlierPrice = (Price)priceSerializer.Deserialize(priceReader);

        optionsParams.Symbol = underlierPrice.Symbol;
        optionsParams.Strike = selectedOption.Strike;
        optionsParams.Contracts = (int)Math.Round((optionsParams.Cashdown / underlierPrice.Close) / 100.00, 0);
        optionsParams.Premium = optionsParams.Shares*selectedOption.Bid;
        optionsParams.ExpirationDate = selectedOption.Expiration;

        // volatility
        DateGenerator dateGenerator = new DateGenerator();
        int daysToExpiration = dateGenerator.DaysBetweenActual(optionsParams.ExpirationDate, underlierPrice.Date);
        prices = PricingDA.GetPrices(underlierPrice.Symbol, underlierPrice.Date, daysToExpiration);

        if(DateTime.Now.Date>=optionsParams.ExpirationDate.Date)currentPrice=PricingDA.GetPrice(underlierPrice.Symbol,optionsParams.ExpirationDate);
        else currentPrice = PriceCache.GetInstance().GetLatestPrice(underlierPrice.Symbol);
        optionsParams.VolatilityDays = daysToExpiration;

        float[] pricesAr = prices.GetPrices();
        optionsParams.Volatility = Numerics.Volatility(ref pricesAr);

        if(DateTime.Now.Date>=optionsParams.ExpirationDate.Date)prices=PricingDA.GetPrices(underlierPrice.Symbol,optionsParams.ExpirationDate,daysToExpiration);   
        else prices = PricingDA.GetPrices(underlierPrice.Symbol,daysToExpiration);

        EquityPriceShocks equityPriceShocks = MarketData.MarketDataModel.EquityPriceShocks.CreateEquityPriceShocks(underlierPrice, optionsParams);
        equityPriceShocksCollection = new ObservableCollection<EquityPriceShock>(equityPriceShocks);

        if (null != underlierPrice) base.DisplayName = "Options Worksheet" + "(" + underlierPrice.Symbol + ")";

        if (null == optionsParams || null == currentPrice) transactionGL=double.NaN;
        else
        {
          if (optionsParams.Strike < currentPrice.Close)transactionGL=optionsParams.Premium + ((optionsParams.Strike * optionsParams.Shares) - (currentPrice.Close * optionsParams.Shares));
          else transactionGL=optionsParams.Premium + ((currentPrice.Close * optionsParams.Shares) - (underlierPrice.Close * optionsParams.Shares));
        }
        base.OnPropertyChanged("Strike");
        base.OnPropertyChanged("Bid");
        base.OnPropertyChanged("Investment");
        base.OnPropertyChanged("ExpirationDate");
        base.OnPropertyChanged("Shares");
        base.OnPropertyChanged("Contracts");
        base.OnPropertyChanged("Premium");
        base.OnPropertyChanged("EquityPriceShocks");
        base.OnPropertyChanged("UnderlierPrice");
        base.OnPropertyChanged("UnderlierPriceDate");
        base.OnPropertyChanged("CurrentPrice");
        base.OnPropertyChanged("CurrentPriceDate");
        base.OnPropertyChanged("OpenInterest");
        base.OnPropertyChanged("DisplayName");
        base.OnPropertyChanged("Volatility");
        base.OnPropertyChanged("ExpirationDateBackground");
        base.OnPropertyChanged("Close");
      }
      catch (Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
      }
    }
// ******************************************************************************************************************************************************
// *************************************************************** M E N U   S U P P O R T **************************************************************
// ******************************************************************************************************************************************************
    public ObservableCollection<TradeBlotter.Model.MenuItem> MenuItems
    {
      get
      {
        ObservableCollection<TradeBlotter.Model.MenuItem> collection = new ObservableCollection<TradeBlotter.Model.MenuItem>();
        collection.Add(new TradeBlotter.Model.MenuItem() { Text = "Display Moving Average", MenuItemClickedCommand = DisplayMovingAverage, StaysOpenOnClick = false });
        collection.Add(new TradeBlotter.Model.MenuItem() { Text = "Display Bollinger Bands", MenuItemClickedCommand = DisplayBollingerBands, StaysOpenOnClick = false });
        return collection;
      }
    }
    public bool UseRealPrice
    {
      get { return useRealPrice; }
      set { useRealPrice = value; base.OnPropertyChanged("UseRealPrice"); }
    }
    public ICommand DisplayMovingAverage
    {
      get
      {
        if (null == displayMovingAverageCommand)
        {
          displayMovingAverageCommand = new RelayCommand(param =>
            {
              SaveParameters saveParams = new SaveParameters();
              saveParams.Add(new KeyValuePair<String, String>("Type", "TradeBlotter.ViewModels.MovingAverageViewModel"));
              saveParams.Add(new KeyValuePair<String, String>("SelectedSymbol", underlierPrice.Symbol));
              saveParams.Add(new KeyValuePair<String, String>("SelectedWatchList", Constants.CONST_ALL));
              saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", "180"));
              saveParams.Add(new KeyValuePair<String, String>("IsLegendVisible", "true"));
              saveParams.Referer=this;
              WorkspaceInstantiator.Invoke(saveParams);
            }, param => { return null == underlierPrice ? false : true; });
        }
        return displayMovingAverageCommand;
      }
    }
    public ICommand DisplayBollingerBands
    {
      get
      {
        if(null==displayBollingerBandCommand)
        {
          displayBollingerBandCommand = new RelayCommand(param =>
            {
              SaveParameters saveParams = new SaveParameters();
              saveParams.Add(new KeyValuePair<String, String>("Type", "TradeBlotter.ViewModels.BollingerBandViewModel"));
              saveParams.Add(new KeyValuePair<String, String>("SelectedSymbol", underlierPrice.Symbol));
              saveParams.Add(new KeyValuePair<String, String>("SelectedWatchList", Constants.CONST_ALL));
              saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", "90"));
              saveParams.Add(new KeyValuePair<String, String>("SyncTradeToBand", "false"));
              saveParams.Add(new KeyValuePair<String, String>("ShowTradeLabels", "true"));
              saveParams.Add(new KeyValuePair<String, String>("IsLegendVisible", "true"));
              saveParams.Referer=this;
              WorkspaceInstantiator.Invoke(saveParams);
              ;
            }, param => { return null == underlierPrice ? false : true; });
        }
        return displayBollingerBandCommand;
      }
    }
    public bool BusyIndicator
    {
      get { return busyIndicator; }
      set
      {
        busyIndicator = value;
        base.OnPropertyChanged("BusyIndicator");
      }
    }
    public CompositeDataSource Close
    {
      get
      {
        if (null == prices) return null;
        CompositeDataSource compositeDataSource = PricesModel.Close(prices);
        return compositeDataSource;
      }
    }
    public ObservableCollection<EquityPriceShock> EquityPriceShocks
    {
      get
      {
        return equityPriceShocksCollection;
      }
      set
      {
        equityPriceShocksCollection = value;
        base.OnPropertyChanged("EquityPriceShocks");
      }
    }
    private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
    {
      if (eventArgs.PropertyName.Equals("UseRealPrice"))
      {
        if (useRealPrice) priceTimer.Start();
        else priceTimer.Stop();
      }
    }
    public String TransactionGL
    {
      get
      {
        if (double.NaN.Equals(transactionGL)) return Constants.CONST_DASHES;
        if (optionsParams.Strike < currentPrice.Close)return Utility.FormatCurrency(transactionGL);
        return Utility.FormatCurrency(transactionGL);
      }
    }
    public String Volatility
    {
      get
      {
        if (null == optionsParams) return Constants.CONST_DASHES;
        return Utility.FormatNumber(optionsParams.Volatility, 2, true);
      }
    }
    public String VolatilityDays
    {
      get
      {
        if (null == optionsParams) return Constants.CONST_DASHES;
        return Utility.FormatNumber(optionsParams.VolatilityDays,0, true);
      }
    }
    public String OpenInterest
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatNumber(selectedOption.OpenInterest,0,true);
      }
    }
    public String CurrentPrice
    {
      get
      {
        if (null == currentPrice) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(currentPrice.Close);
      }
    }
    public String CurrentPriceDate
    {
      get
      {
        if (null == currentPrice) return Constants.CONST_DASHES;
        return Utility.DateTimeToStringMMSDDSYYYYHHMMSS(currentPrice.Date);
      }
    }
    public String LossPoint
    {
      get
      {
        if (null==selectedOption || null == underlierPrice) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(underlierPrice.Close-selectedOption.Bid);
      }
    }
    public String UnderlierPrice
    {
      get
      {
        if (null == underlierPrice) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(underlierPrice.Close);
      }
    }
    public String UnderlierPriceDate
    {
      get
      {
        if (null == underlierPrice) return Constants.CONST_DASHES;
        return Utility.DateTimeToStringMMSDDSYYYY(underlierPrice.Date);
      }
    }
    public String Premium
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(optionsParams.Premium);
      }
    }
    public String Shares
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatNumber(optionsParams.Shares,2,true);
      }
    }
    public String Contracts
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatNumber(optionsParams.Contracts, 0, true);
      }
    }
    public String ExpirationDate
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.DateTimeToStringMMSDDSYYYY(selectedOption.Expiration);
      }
    }
    public Brush TransactionGLBackground
    {
      get
      {
        if (null == underlierPrice) return null;
        return underlierPrice.Date < DateTime.Now ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
      }
    }
    public Brush ExpirationDateBackground
    {
      get
      {
        return DateTime.Now.Date>=optionsParams.ExpirationDate.Date?new SolidColorBrush(Colors.Red):new SolidColorBrush(Colors.Green);
      }
    }
    public String Strike
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(selectedOption.Strike);
      }
    }
    public String Bid
    {
      get
      {
        if (null == selectedOption) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(selectedOption.Bid);
      }
    }
    public String Investment
    {
      get
      {
        if (null == optionsParams) return Constants.CONST_DASHES;
        return Utility.FormatCurrency(optionsParams.Cashdown);
      }
    }

    public void OnErrorItemHandler(String symbol, String message)
    {
    }
    public override String Title
    {
      get
      {
        if (null == companyName || null == optionsParams) return "Options Worksheet";
        return companyName + " (" + optionsParams.Symbol + ")";
      }
    }
  }
}
