using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using Eremex.AvaloniaUI.Charts;
using MarketData;
using MarketData.CNNProcessing;
using MarketData.DataAccess;
using MarketData.Generator;
using MarketData.MarketDataModel;
using MarketData.Numerical;
using MarketData.Utils;
using PortfolioManager.Cache;
using PortfolioManager.DataSeriesViewModels;
using PortfolioManager.Extensions;
using PortfolioManager.Models;
using PortfolioManager.UIUtils;
using SkiaSharp;

namespace PortfolioManager.ViewModels
{
  public static class TextMarkerImageGenerator
  {
    public static IImage GenerateImage(String text)
    {
      ImageHelper imageHelper = new ImageHelper();
      int fontSize = 36;
      int width = 640;
      imageHelper.CreateImage(width, fontSize);
      imageHelper.Fill(SKColors.White);
      SKTextAlign align = SKTextAlign.Center;
      SKFont font = new SKFont(SKTypeface.FromFamilyName("Helvetica"), fontSize);
      imageHelper.DrawText(text, new SKPoint(width / 2, fontSize - 2), SKColors.Black, align, font);
      Avalonia.Media.Imaging.Bitmap avBitmap = new Avalonia.Media.Imaging.Bitmap(imageHelper.ToStream());
      return avBitmap;
      //      avBitmap.Save("c:\\3\\mybitmap.jpg");    }
    }

    public static IImage GenerateImage(int width, int height,SKColor color)
    {
      ImageHelper imageHelper = new ImageHelper();
      imageHelper.CreateImage(width, height);
      imageHelper.Fill(color);
      Avalonia.Media.Imaging.Bitmap avBitmap = new Avalonia.Media.Imaging.Bitmap(imageHelper.ToStream());
      return avBitmap;
      //      avBitmap.Save("c:\\3\\mybitmap.jpg");    }
    }
  }

  public partial class BollingerBandViewModel : WorkspaceViewModel
    {
      private bool isBusy = false;
      private List<String> watchListNames;
      private String selectedWatchList;
      private List<Int32> dayCounts = new int[] { 60, 90, 180, 360, 720, 1440, 3600 }.ToList<int>();
      private int selectedDayCount = 90;
      private ObservableCollection<String> symbols = new ObservableCollection<String>();
      private String selectedSymbol = default;
      private bool showMarkers = false;

      private InsiderTransactionSummaries insiderTransactionSummaries = null;
      private Price zeroPrice = null;
      private PortfolioTrades portfolioTrades;
      private PortfolioTrades portfolioTradesLots;
      private StopLimit stopLimit;     // This is the stop limit that is looked up in the database and displayed (if there is one)
      private StopLimits stopLimits;   // These stop limits might be passed in with the SaveParams. (i.e.) MMTRend model passes in StopLimits.  If these are passsed in then they are displayed instead of stopLimit.    
      private Prices prices = null;

      private bool syncTradeToBand = true;
      private String companyName = default;
      private bool showTradeLabels = true;

      private bool useLeastSquaresFit = true;
      private bool showInsiderTransactions = true;

      private CompositeDataSource compositeDataSourceZeroPoint = null;
      private CompositeDataSource compositeDataSourceStopLimit = null;

      private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedSmall = null;
      private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedMedium = null;
      private CompositeDataSource compositeDataSourceInsiderTransactionPointDisposedLarge = null;
      private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredSmall = null;
      private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredMedium = null;
      private CompositeDataSource compositeDataSourceInsiderTransactionPointAcquiredLarge = null;

      private CompositeDataSource compositeDataSourceK = null;
      private CompositeDataSource compositeDataSourceKL1 = null;
      private CompositeDataSource compositeDataSourceL = null;
      private CompositeDataSource compositeDataSourceLP1 = null;
      private CompositeDataSource compositeDataSourceHigh = null;
      private CompositeDataSource compositeDataSourceLow = null;
      private CompositeDataSource compositeDataSourceClose = null;
      private CompositeDataSource compositeDataSourceSMAN = null;
      private CompositeDataSource compositeDataSourceVolume = null;
      private CompositeDataSource compositeDataSourceLeastSquares = null;
      private CompositeDataSource compositeDataSourceTradePoints = null;

      private BollingerBands bollingerBands;
      
      public BollingerBandViewModel()
      {
        InitializeDataSources();
        PropertyChanged += OnViewModelPropertyChanged;
        DisplayName = "Bollinger";
        Initialize(true);
      }

      public BollingerBandViewModel(bool loadedFromParams = false)
      {
        InitializeDataSources();
        PropertyChanged += OnViewModelPropertyChanged;
        DisplayName = "Bollinger";
        Initialize(false);
      }

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

      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");
          }
        });
      }

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

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

      public String GraphTitle
      {
        get
        {
          if (null == companyName || null == prices || 0 == prices.Count) 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(prices.Take(2).ToList());
          if (2 == prices2day.Count) change = prices2day.GetReturns()[0];
          sb.Append(displayCompanyName);
          sb.Append(" (").Append(selectedSymbol).Append(") ");
          sb.Append(Utility.DateTimeToStringMMHDDHYYYY(prices[prices.Count - 1].Date));
          sb.Append(" Thru ");
          sb.Append(Utility.DateTimeToStringMMHDDHYYYY(prices[0].Date));
          sb.Append(" (").Append(Utility.FormatCurrency(prices[0].Close));
          sb.Append("/").Append(Utility.FormatCurrency(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 override String DisplayName
      {
        get
        {
          return "Bollinger Band";
        }
      }

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

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

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

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

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

      public bool SyncTradeToBand
      {
        get
        {
          return syncTradeToBand;
        }
        set
        {
          syncTradeToBand = value;
          if (syncTradeToBand) showTradeLabels = true;
          base.OnPropertyChanged("SyncTradeToBand");
          base.OnPropertyChanged("TradePoints");
          base.OnPropertyChanged("ZeroPoint");
          base.OnPropertyChanged("StopLimits");
          base.OnPropertyChanged("TradePointMarkers");
          base.OnPropertyChanged("ZeroPointMarkers");
          base.OnPropertyChanged("StopLimitMarkers");
//          base.OnPropertyChanged("ZeroPointMarkersTextMarkers");
        }
      }

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

      public Boolean CheckBoxShowInsiderTransactions
      {
        get
        {
          return showInsiderTransactions;
        }
        set
        {
          showInsiderTransactions = value;
          base.OnPropertyChanged("CheckBoxShowInsiderTransactions");
          base.OnPropertyChanged("InsiderTransactionPointDisposedSmall");
          base.OnPropertyChanged("InsiderTransactionPointDisposedMedium");
          base.OnPropertyChanged("InsiderTransactionPointDisposedLarge");
          base.OnPropertyChanged("InsiderTransactionPointAcquiredSmall");
          base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium");
          base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge");

          base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall");
          base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium");
          base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge");
          base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredSmall");
          base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredMedium");
          base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredLarge");
        }
      }


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

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

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

      /// <summary>
      /// See the XAML and also OSValueConverter in UIUtils for an explanation.
      /// </summary>
      public int MarkerSize
      {
        get
        {
          return 0;
        }
      }

      // *********************************************************************************************************************************************


      // ******************************************************************* 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 = true;
            }
            catch (Exception) { syncTradeToBand = true; }
            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()));
            }
            // base.OnPropertyChanged("SelectedWatchList");
            // base.OnPropertyChanged("SelectedSymbol");
          });
          workerTask.ContinueWith((continuation) =>
          {
            base.OnPropertyChanged("SelectedWatchList");
            base.OnPropertyChanged("SelectedSymbol");
          });
        }
        catch (Exception)
        {
        }
      }

      // ****************************************************** R E L A Y S ********************************************************
      [RelayCommand]
      public async Task Refresh()
      {
        base.OnPropertyChanged("SelectedSymbol");
        await Task.FromResult(true);
      }

      // *************************************************************************************************************************************
      private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
      {
        if (eventArgs.PropertyName.Equals("SelectedSymbol") && !String.IsNullOrEmpty(selectedSymbol))
        {
          InitializeDataSources();
          InitializeData();
        }

        if ((eventArgs.PropertyName.Equals("SyncTradeToBand") ||
            eventArgs.PropertyName.Equals("ShowTradeLabels") ||
            eventArgs.PropertyName.Equals("SelectedSymbol") ||
            eventArgs.PropertyName.Equals("ShowRiskFree") ||
            eventArgs.PropertyName.Equals("LeastSquaresFit") ||
            eventArgs.PropertyName.Equals("SelectedDayCount"))
            && !String.IsNullOrEmpty(selectedSymbol))
        {
          IsBusy = true;
          Task workerTask = Task.Factory.StartNew(() =>
          {
            MDTrace.WriteLine(LogLevel.DEBUG, $"OnViewModelPropertyChanged({eventArgs.PropertyName}).  Selected symbol '{selectedSymbol}'");
            base.DisplayName = "Bollinger(" + selectedSymbol + ")";
            base.OnPropertyChanged("DisplayName");
            stopLimit = PortfolioDA.GetStopLimit(selectedSymbol);
            portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol);
            portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades);
            if (null != portfolioTrades && 0 != portfolioTrades.Count)
            {
              DateGenerator dateGenerator = new DateGenerator();
              DateTime earliestTrade = portfolioTrades[0].TradeDate;
              earliestTrade = earliestTrade.AddDays(-30);
              int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now);
              if (daysBetween < selectedDayCount || !syncTradeToBand) prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount);
              else prices = PricingDA.GetPrices(selectedSymbol, earliestTrade);

              DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30);
              insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);

              // calculate the break even price on the open trades for this symbol
              PortfolioTrades openTrades = portfolioTrades.GetOpenTrades();
              DateTime latestPricingDate = PricingDA.GetLatestDate(selectedSymbol);
              Price latestPrice = PricingDA.GetPrice(selectedSymbol, latestPricingDate);
              zeroPrice = ParityGenerator.GenerateGainLossValue(openTrades, latestPrice);

              if (!syncTradeToBand)
              {
                DateTime earliestPricingDate = prices[prices.Count - 1].Date;
                earliestPricingDate = earliestPricingDate.AddDays(30);
                IEnumerable<PortfolioTrade> tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade);
                portfolioTrades = new PortfolioTrades();
                foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade);
                portfolioTradesLots = portfolioTrades;
              }
            }
            else
            {
              prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount);
              if (null != prices && 0 != prices.Count)
              {
                DateGenerator dateGenerator = new DateGenerator();
                DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30);
                insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);
              }
            }
            companyName = PricingDA.GetNameForSymbol(selectedSymbol);
            bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices);
            CreateCompositeDataSources();
          });
          workerTask.ContinueWith((continuation) =>
          {
            base.OnPropertyChanged("K");
            base.OnPropertyChanged("KL1");
            base.OnPropertyChanged("L");
            base.OnPropertyChanged("LP1");
            base.OnPropertyChanged("High");
            base.OnPropertyChanged("Low");
            base.OnPropertyChanged("Close");
            base.OnPropertyChanged("SMAN");
            base.OnPropertyChanged("Volume");
            base.OnPropertyChanged("LeastSquares");
            base.OnPropertyChanged("GraphTitle");
            base.OnPropertyChanged("Title");

            base.OnPropertyChanged("TradePoints");
            base.OnPropertyChanged("ZeroPoint");
            base.OnPropertyChanged("StopLimits");

            base.OnPropertyChanged("TradePointMarkers");
            base.OnPropertyChanged("ZeroPointMarkers");
            base.OnPropertyChanged("StopLimitMarkers");

//            base.OnPropertyChanged("ZeroPointMarkersTextMarkers");


            base.OnPropertyChanged("InsiderTransactionPointDisposedSmall");
            base.OnPropertyChanged("InsiderTransactionPointDisposedMedium");
            base.OnPropertyChanged("InsiderTransactionPointDisposedLarge");

            base.OnPropertyChanged("InsiderTransactionPointAcquiredSmall");
            base.OnPropertyChanged("InsiderTransactionPointAcquiredMedium");
            base.OnPropertyChanged("InsiderTransactionPointAcquiredLarge");

            base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedSmall");
            base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedMedium");
            base.OnPropertyChanged("InsiderTransactionPointMarkersDisposedLarge");

            base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredSmall");
            base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredMedium");
            base.OnPropertyChanged("InsiderTransactionPointMarkersAcquiredLarge");
            IsBusy = false;
          });
        }
        else if (eventArgs.PropertyName.Equals("SelectedWatchList"))
        {
          IsBusy = true;
          Task workerTask = Task.Factory.StartNew(() =>
          {
            if (selectedWatchList.Equals(Constants.CONST_ALL))
            {
              symbols.Clear();
              symbols.AddRange(SymbolCache.GetInstance().GetSymbols());
            }
            else
            {
              symbols.Clear();
              symbols.AddRange(WatchListDA.GetWatchList(selectedWatchList));
            }
          });
          workerTask.ContinueWith((continuation) =>
          {
            IsBusy = false;
            base.OnPropertyChanged("Symbols");
          });
        }
      }

      // ************************************************* C O M P O S I T E  D A T A S O U R C E S  ********************************************

      public CompositeDataSource K
      {
        get
        {
          return compositeDataSourceK;
        }
      }
      public CompositeDataSource KL1
      {
        get
        {
          return compositeDataSourceKL1;
        }
      }

      public CompositeDataSource L
      {
        get
        {
          return compositeDataSourceL;
        }
      }
      public CompositeDataSource LP1
      {
        get
        {
          return compositeDataSourceLP1;
        }
      }
      public CompositeDataSource High
      {
        get
        {
          return compositeDataSourceHigh;
        }
      }
      public CompositeDataSource Low
      {
        get
        {
          return compositeDataSourceLow;
        }
      }
      public CompositeDataSource Close
      {
        get
        {
          return compositeDataSourceClose;
        }
      }
      public CompositeDataSource SMAN
      {
        get
        {
          return compositeDataSourceSMAN;
        }
      }
      public CompositeDataSource Volume
      {
        get
        {
          return compositeDataSourceVolume;
        }
      }

      public CompositeDataSource LeastSquares
      {
        get
        {
          if (!useLeastSquaresFit || null == bollingerBands) return Empty();
          return compositeDataSourceLeastSquares;
        }
      }

      public CompositeDataSource TradePoints
      {
        get
        {
          if (!showTradeLabels) return Empty();
          return compositeDataSourceTradePoints;
        }
      }

      public CompositeDataSource ZeroPoint
      {
        get
        {
          if (!showTradeLabels) return Empty();
          return compositeDataSourceZeroPoint;
        }
      }

      public CompositeDataSource StopLimits
      {
        get
        {
          if (!showTradeLabels) return Empty();
          return compositeDataSourceStopLimit;
        }
      }

      public CompositeDataSource InsiderTransactionPointDisposedSmall
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointDisposedSmall;
        }
      }

      public CompositeDataSource InsiderTransactionPointDisposedMedium
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointDisposedMedium;
        }
      }

      public CompositeDataSource InsiderTransactionPointDisposedLarge
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointDisposedLarge;
        }
      }

      public CompositeDataSource InsiderTransactionPointAcquiredSmall
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointAcquiredSmall;
        }
      }

      public CompositeDataSource InsiderTransactionPointAcquiredMedium
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointAcquiredMedium;
        }
      }

      public CompositeDataSource InsiderTransactionPointAcquiredLarge
      {
        get
        {
          if (!showInsiderTransactions) return Empty();
          return compositeDataSourceInsiderTransactionPointAcquiredLarge;
        }
      }

      // ******************************************************** M A R K E R S  **************************************************

      public IImage StopLimitMarkers
      {
        get
        {
          if (!showTradeLabels) return null;
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleUp);
        }
      }

      /// <summary>
      /// This is just a single marker
      /// </summary>
      public IImage ZeroPointMarkers
      {
        get
        {
          if (!showTradeLabels) return null;
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.BlueTriangleUp);
        }
      }
      
      // public IImage ZeroPointMarkersTextMarkers
      // {
      //   get
      //   {
      //     return TextMarkerImageGenerator.GenerateImage(64,64,SKColors.Red);
      //   }
      // }

      /// <summary>
      /// This is just a single marker
      /// </summary>
      // public IImage ZeroPointMarkersTextMarkers
      // {
      //   get
      //   {
      //     if (null == zeroPrice || !showTradeLabels) return null;
      //     StringBuilder sb = new StringBuilder();
      //     sb.Append("Even ");
      //     sb.Append(Utility.FormatCurrency(zeroPrice.Close));
      //     Price latestPrice = prices[0];
      //     double parityOffsetPercent = (latestPrice.Close - zeroPrice.Close) / zeroPrice.Close;
      //     sb.Append("(").Append(parityOffsetPercent < 0 ? "" : "+").Append(Utility.FormatPercent(parityOffsetPercent)).Append(")");
      //     return TextMarkerImageGenerator.GenerateImage(sb.ToString());
      //   }
      // }

    public IImage TradePointMarkers
    {
      get
      {
        if (!showTradeLabels) return null;
        return ImageCache.GetInstance().GetImage(ImageCache.ImageType.YellowTriangleUp);
      }
    }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersDisposedSmall
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleDown);
        }
      }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersDisposedMedium
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleDown);
        }
      }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersDisposedLarge
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleDown);
        }
      }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersAcquiredSmall
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.GreenTriangleUp);
        }
      }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersAcquiredMedium
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.GreenTriangleUp);
        }
      }

      /// <summary>
      /// This size is controlled in the XAML
      /// </summary>
      public IImage InsiderTransactionPointMarkersAcquiredLarge
      {
        get
        {
          return ImageCache.GetInstance().GetImage(ImageCache.ImageType.GreenTriangleUp);
        }
      }

      // *********************************************************************************************************************************************
      public void CreateCompositeDataSources()
      {
        if (null == prices || 0 == prices.Count) return;
        double minClose = (from Price price in prices select price.Close).Min();

        // get the maximum date in the bollinger band series
        DateTime maxBollingerDate = (from BollingerBandElement bollingerBandElement in bollingerBands select bollingerBandElement.Date).Max();
        // ensure that the insider transactions are clipped to the bollingerband max date.  There are some items in insider transaction summaries (options dated in the future) that will throw the graphic out of proportion
        InsiderTransactionSummaries disposedSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed < 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());
        InsiderTransactionSummaries acquiredSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed > 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());

        BinCollection<InsiderTransactionSummary> disposedSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(disposedSummaries), 3);
        BinCollection<InsiderTransactionSummary> acquiredSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(acquiredSummaries), 3);

        compositeDataSourceZeroPoint = GainLossModel.Price(zeroPrice);

        if (null != stopLimits)
        {
          compositeDataSourceStopLimit = StopLimitCompositeModel.CreateCompositeDataSource(stopLimits);
        }
        else if (null != stopLimit && null != zeroPrice)
        {
          compositeDataSourceStopLimit = GainLossModel.CreateCompositeDataSource(zeroPrice.Date, stopLimit.StopPrice);
        }

        compositeDataSourceInsiderTransactionPointDisposedSmall = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[2]), minClose);
        compositeDataSourceInsiderTransactionPointDisposedMedium = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[1]), minClose);
        compositeDataSourceInsiderTransactionPointDisposedLarge = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(disposedSummariesBin[0]), minClose);
        compositeDataSourceInsiderTransactionPointAcquiredSmall = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[0]), minClose);
        compositeDataSourceInsiderTransactionPointAcquiredMedium = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[1]), minClose);
        compositeDataSourceInsiderTransactionPointAcquiredLarge = InsiderTransactionModel.InsiderTransactionSummaries(new InsiderTransactionSummaries(acquiredSummariesBin[2]), minClose);

        compositeDataSourceK = BollingerBandModel.K(bollingerBands);
        compositeDataSourceKL1 = BollingerBandModel.KL1(bollingerBands);
        compositeDataSourceL = BollingerBandModel.L(bollingerBands);
        compositeDataSourceLP1 = BollingerBandModel.LP1(bollingerBands);
        compositeDataSourceHigh = BollingerBandModel.High(bollingerBands);
        compositeDataSourceLow = BollingerBandModel.Low(bollingerBands);
        compositeDataSourceClose = BollingerBandModel.Close(bollingerBands);
        compositeDataSourceSMAN = BollingerBandModel.SMAN(bollingerBands);
        compositeDataSourceVolume = BollingerBandModel.Volume(bollingerBands);

        compositeDataSourceLeastSquares = BollingerBandModel.LeastSquares(bollingerBands);

        compositeDataSourceTradePoints = PortfolioTradeModel.PortfolioTrades(portfolioTradesLots);
      }

      private void InitializeData()
      {

        insiderTransactionSummaries = null;
        zeroPrice = null;
        prices = null;
        portfolioTrades = null;
        portfolioTradesLots = null;
        stopLimit = null;
      }

      private void InitializeDataSources()
      {
        compositeDataSourceStopLimit = Empty();
        compositeDataSourceZeroPoint = Empty();

        compositeDataSourceInsiderTransactionPointDisposedSmall = Empty();
        compositeDataSourceInsiderTransactionPointDisposedMedium = Empty();
        compositeDataSourceInsiderTransactionPointDisposedLarge = Empty();

        compositeDataSourceInsiderTransactionPointAcquiredSmall = Empty();
        compositeDataSourceInsiderTransactionPointAcquiredMedium = Empty();
        compositeDataSourceInsiderTransactionPointAcquiredLarge = Empty();

        compositeDataSourceK = Empty();
        compositeDataSourceKL1 = Empty();
        compositeDataSourceL = Empty();
        compositeDataSourceLP1 = Empty();
        compositeDataSourceHigh = Empty();
        compositeDataSourceLow = Empty();
        compositeDataSourceClose = Empty();
        compositeDataSourceSMAN = Empty();
        compositeDataSourceVolume = Empty();
        compositeDataSourceLeastSquares = Empty();
        compositeDataSourceTradePoints = Empty();
      }

      private static CompositeDataSource Empty()
      {
        return new CompositeDataSource()
        {
          DataAdapter = new SortedDateTimeDataAdapter()
        };
      }
    }
}
