using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using MarketData;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using PortfolioManager.Extensions;
using PortfolioManager.Renderers;
using PortfolioManager.UIUtils;

namespace PortfolioManager.ViewModels
{
    public partial class BollingerBandViewModel : PlotterWorkspaceViewModel
    {
      private List<String> watchListNames;
      private String selectedWatchList;
      private ObservableCollection<String> symbols = new ObservableCollection<String>();
      private List<Int32> dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList<int>();
      private int selectedDayCount = 90;
      private bool isBusy = false;
      private String selectedSymbol = default;
      private String companyName = default;
      private BollingerBandRenderer bollingerBandRenderer = default;
      private bool showInsiderTransactions = true;
      private bool showTradeLabels = true;
      private bool syncTradeToBand = false;
      private bool useLeastSquaresFit = true;
      private StopLimits stopLimits = default;

      public BollingerBandViewModel()
      {
        DisplayName = "Bollinger";
        OnPlotterLoadedEventHandler += PlotterLoadedEvent;
        PropertyChanged += OnViewModelPropertyChanged;
        Initialize();
      }

      protected override void OnDispose()
      {
        MDTrace.WriteLine(LogLevel.DEBUG, $"Dispose BollingerBandViewModel");
        base.OnDispose();
      }

      public override String Title
      {
        get
        {
          if (null == selectedSymbol) return DisplayName;
          return "Bollinger " + "(" + selectedSymbol + ")";
        }
      }    

      public override String DisplayName
      {
        get
        {
          return "Bollinger Band";
        }
      }        

      private void Initialize(bool executePropertyChanged = true)
      {
        Task workerTask = Task.Factory.StartNew(() =>
        {
          MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::Initialize()");
          watchListNames = WatchListDA.GetWatchLists();
          watchListNames.Insert(0, UIConstants.CONST_ALL);
          selectedWatchList = watchListNames.Find(x => x.Equals("Valuations"));
          symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
        });
        workerTask.ContinueWith((continuation) =>
        {
          if (executePropertyChanged)
          {
            base.OnPropertyChanged("Symbols");
            base.OnPropertyChanged("WatchListNames");
            base.OnPropertyChanged("SelectedWatchList");
          }
        });
      }


      // *******************************************************************************************************************************************     
      /// <summary>
      /// This event will be called the first time the view is rendered and each time the view comes back into focus (i.e.) Tab activated
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      public void PlotterLoadedEvent(object sender, PlotterLoadedEventArgs e)
      {
        base.OnPropertyChanged("Plotter");
        if (default != selectedSymbol)
        {
          base.OnPropertyChanged("SelectedSymbol");
        }
      }

      private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
      {
          if(eventArgs.PropertyName.Equals("SelectedWatchList"))
          {
              IsBusy=true;
              
              Task workerTask = Task.Factory.StartNew(()=>
              {
                symbols.Clear();
                if(UIConstants.CONST_ALL.Equals(selectedWatchList))
                {
                  symbols.AddRange(PricingDA.GetSymbols());
                }
                else
                {
                  symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
                }
              });
              workerTask.ContinueWith((continuation)=>
              {
                IsBusy=false;
                base.OnPropertyChanged("Symbols");
              });
          }
          else if ((eventArgs.PropertyName.Equals("SyncTradeToBand") ||
              eventArgs.PropertyName.Equals("ShowTradeLabels") ||
              eventArgs.PropertyName.Equals("SelectedSymbol") ||
              eventArgs.PropertyName.Equals("ShowRiskFree") ||
              eventArgs.PropertyName.Equals("UseLeastSquaresFit") ||
              eventArgs.PropertyName.Equals("CheckBoxShowInsiderTransactions") ||
              eventArgs.PropertyName.Equals("SelectedDayCount"))
              && !String.IsNullOrEmpty(selectedSymbol)
              && default != Plotter)
          {
              IsBusy = true;
              Task workerTask = Task.Factory.StartNew(() =>
              {
                  companyName = PricingDA.GetNameForSymbol(selectedSymbol);
                  bollingerBandRenderer = new BollingerBandRenderer(Plotter);
                  bollingerBandRenderer.ShowLeastSquares = useLeastSquaresFit;
                  bollingerBandRenderer.SyncTradeToBand = syncTradeToBand;
                  bollingerBandRenderer.ShowInsiderTransactions = showInsiderTransactions;
                  bollingerBandRenderer.ShowTradeLabels = showTradeLabels;
                  bollingerBandRenderer.ExternalStopLimits = stopLimits;
                  bollingerBandRenderer.SetData(selectedSymbol, selectedDayCount);
                  bollingerBandRenderer.Render();
              });
              workerTask.ContinueWith((continuation) =>
              {
                base.OnPropertyChanged("GraphTitle");
                base.OnPropertyChanged("Title");
                IsBusy = false;
              });
          }
      }

    // ********************************************** 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();
        if (String.IsNullOrEmpty(selectedSymbol)) return null;
        saveParams.Add(new KeyValuePair<String, String>("Type", GetType().Namespace + "." + GetType().Name));
        saveParams.Add(new KeyValuePair<String, String>("SelectedSymbol", selectedSymbol));
        saveParams.Add(new KeyValuePair<String, String>("SelectedWatchList", selectedWatchList));
        saveParams.Add(new KeyValuePair<String, String>("SelectedDayCount", selectedDayCount.ToString()));
        saveParams.Add(new KeyValuePair<String, String>("SyncTradeToBand", syncTradeToBand.ToString()));
        saveParams.Add(new KeyValuePair<String, String>("ShowTradeLabels", showTradeLabels.ToString()));
        saveParams.Add(new KeyValuePair<String, String>("UseLeastSquaresFit", useLeastSquaresFit.ToString()));
        saveParams.Add(new KeyValuePair<String, String>("ShowInsiderTransactions", showInsiderTransactions.ToString()));
        if (null != stopLimits && 0 != stopLimits.Count)
        {
          saveParams.Add(new KeyValuePair<String, String>("StopHistoryCount", stopLimits.Count.ToString()));
          for (int index = 0; index < stopLimits.Count; index++)
          {
            String strItemKey = String.Format("StopHistory_{0}", index);
            StopLimit stopLimit = stopLimits[index];
            NVPCollection nvpCollection = stopLimit.ToNVPCollection();
            String strStopHistoryItem = nvpCollection.ToString();
            saveParams.Add(new KeyValuePair<String, String>(strItemKey, strStopHistoryItem));
          }
        }
      return saveParams;    
    }

    public override void SetSaveParameters(SaveParameters saveParameters)
    {
        try
        {

          Task workerTask = Task.Factory.StartNew(() =>
          {
            Referer = saveParameters.Referer;
            selectedSymbol = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedSymbol") select item).FirstOrDefault().Value;
            selectedWatchList = (from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedWatchList") select item).FirstOrDefault().Value;
            selectedDayCount = Int32.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SelectedDayCount") select item).FirstOrDefault().Value);
            MDTrace.WriteLine(LogLevel.DEBUG, $"BollingerBandViewModel::SetSaveParameters('{selectedSymbol}','{selectedWatchList}','{selectedDayCount}')");
            try
            {
              if (saveParameters.ContainsKey("SyncTradeToBand")) syncTradeToBand = Boolean.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("SyncTradeToBand") select item).FirstOrDefault().Value);
              else syncTradeToBand = false;
            }
            catch (Exception) { syncTradeToBand = false; }
            try
            {
              if (saveParameters.ContainsKey("ShowTradeLabels")) showTradeLabels = Boolean.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("ShowTradeLabels") select item).FirstOrDefault().Value);
              else showTradeLabels = true;
            }
            catch (Exception) { showTradeLabels = true; }
            try
            {
              if (saveParameters.ContainsKey("UseLeastSquaresFit")) useLeastSquaresFit = Boolean.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("UseLeastSquaresFit") select item).FirstOrDefault().Value);
            }
            catch (Exception) {; }
            try
            {
              if (saveParameters.ContainsKey("ShowInsiderTransactions")) showInsiderTransactions = Boolean.Parse((from KeyValuePair<String, String> item in saveParameters where item.Key.Equals("ShowInsiderTransactions") select item).FirstOrDefault().Value);
            }
            catch (Exception) {; }
            try
            {
              if (saveParameters.ContainsKey("StopHistoryCount"))
              {
                stopLimits = StopLimitsExtensions.FromSaveParams(saveParameters);
              }
            }
            catch (Exception exception)
            {
              MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString()));
            }
          });
          workerTask.ContinueWith((continuation) =>
          {
            base.OnPropertyChanged("SelectedWatchList");
            base.OnPropertyChanged("SelectedSymbol");
            base.OnPropertyChanged("Title");
          });
        }
        catch (Exception exception)
        {
          MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString());
        }
    }

      // ****************************************************** P R O P E R T I E S ************************************************

    public String GraphTitle
    {
        get
        {
            if (null == companyName || null == bollingerBandRenderer || null == bollingerBandRenderer.Prices) return "";
            String displayCompanyName = companyName;
            if (displayCompanyName.Length > 40) displayCompanyName = displayCompanyName.Substring(0, 40) + "...";
            StringBuilder sb = new StringBuilder();
            float change = float.NaN;
            Prices prices2day = new Prices(bollingerBandRenderer.Prices.Take(2).ToList());
            if (2 == prices2day.Count) change = prices2day.GetReturns()[0];
            sb.Append(displayCompanyName);
            sb.Append(" (").Append(selectedSymbol).Append(") ");
            sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[bollingerBandRenderer.Prices.Count - 1].Date));
            sb.Append(" Thru ");
            sb.Append(Utility.DateTimeToStringMMHDDHYYYY(bollingerBandRenderer.Prices[0].Date));
            sb.Append(" (").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Close));
            sb.Append("/").Append(Utility.FormatCurrency(bollingerBandRenderer.Prices[0].Low));
            if (!float.IsNaN(change))
            {
                sb.Append(",");
                sb.Append(change >= 0.00 ? "+" : "").Append(Utility.FormatPercent((double)change));
            }
            sb.Append(")");
            return sb.ToString();
        }
    }

    public List<String> WatchListNames
    {
        get
        {
            return watchListNames;
        }
    }

    public String SelectedWatchList
    {
        get
        {
            return selectedWatchList;
        }
        set
        {
            selectedWatchList = value;
            base.OnPropertyChanged("SelectedWatchList");
        }
    }

    public ObservableCollection<String> Symbols
    {
        get
        {
            return symbols;
        }
    }

    public int SelectedDayCount
    {
        get
        {
            return selectedDayCount;
        }
        set
        {
            selectedDayCount = value;
            base.OnPropertyChanged("SelectedDayCount");
        }
    }

    public List<int> DayCounts
    {
        get
        {
            return dayCounts;
        }
    }

    public String SelectedSymbol
    {
      get
      {
          return selectedSymbol;
      }
      set
      {
        if (String.IsNullOrEmpty(value))
        {
            return;
        }
        selectedSymbol = value;
        base.OnPropertyChanged("SelectedSymbol");
      }
    }

    public bool IsBusy
    {
      get
      {
        return isBusy;
      }
      set
      {
        isBusy = value;
        base.OnPropertyChanged("IsBusy");
      }
    }

    public bool SyncTradeToBand
    {
      get
      {
        return syncTradeToBand;
      }
      set
      {
        syncTradeToBand = value;
        base.OnPropertyChanged("SyncTradeToBand");
      }
    }        

    public bool ShowTradeLabels
    {
      get
      {
        return showTradeLabels;
      }
      set
      {
        showTradeLabels = value;
        base.OnPropertyChanged("ShowTradeLabels");
      }
    } 

    public bool UseLeastSquaresFit
    {
      get
      {
        return useLeastSquaresFit;
      }
      set
      {
        useLeastSquaresFit = value;
        base.OnPropertyChanged("UseLeastSquaresFit");
      }
    }

    public Boolean CheckBoxShowInsiderTransactions
    {
      get
      {
        return showInsiderTransactions;
      }
      set
      {
        showInsiderTransactions = value;
        base.OnPropertyChanged("CheckBoxShowInsiderTransactions");
      }
    }               

    [RelayCommand]
    public async Task Refresh()
    {
        base.OnPropertyChanged("SelectedSymbol");
        await Task.FromResult(true);
    }    
  }
}