using System;
using System.Linq;
using Eremex.AvaloniaUI.Charts;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Numerical;
using MarketData.Utils;
using PortfolioManager.DataSeriesViewModels;

namespace PortfolioManager.Models
{
  public class GainLossModel
  {
    private GainLossModel()
    {
    }

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

    public static CompositeDataSource Price(Price price)
    {
      if (null == price) return Empty();
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
      sortedDateTimeDataAdapter.Add(price.Date, price.Close);
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    public static CompositeDataSource CreateCompositeDataSource(DateTime xSource, double ySource)
    {
      if (Utility.IsEpoch(xSource)) return Empty();
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
      sortedDateTimeDataAdapter.Add(xSource, ySource);
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;      
    }    

    // This is the active gain/loss as number or percent.
    public static CompositeDataSource GainLoss(ModelPerformanceSeries gainLossList, bool useGainLoss)
    {
      if (null == gainLossList) return Empty();
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
      foreach (ModelPerformanceItem modelPerformanceItem in gainLossList)
      {
        sortedDateTimeDataAdapter.Add(modelPerformanceItem.Date, useGainLoss ? modelPerformanceItem.CumulativeGainLoss : modelPerformanceItem.CumProdMinusOne * 100.00);
      }
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    // This is the active gain/loss as number or percent.
    public static CompositeDataSource GainLoss(GainLossCompoundModelCollection gainLossList, bool useGainLoss)
    {
      if (null == gainLossList) return Empty();
      GainLossCompoundModelCollection sortedCollection = new GainLossCompoundModelCollection(gainLossList.OrderBy(x => x.Date).ToList());
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
      foreach (GainLossCompoundModel gainLossCompoundModel in sortedCollection)
      {
        sortedDateTimeDataAdapter.Add(gainLossCompoundModel.Date, useGainLoss ? gainLossCompoundModel.ActiveGainLoss : gainLossCompoundModel.ActiveGainLossPercent);
      }
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    // This is the total gain loss as number or percent
    public static CompositeDataSource TotalGainLoss(GainLossCompoundModelCollection gainLossList, bool useGainLoss)
    {
      if (null == gainLossList) return Empty();
      GainLossCompoundModelCollection sortedCollection = new GainLossCompoundModelCollection(gainLossList.OrderBy(x => x.Date).ToList());
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
      foreach (GainLossCompoundModel gainLossCompoundModel in sortedCollection)
      {
        sortedDateTimeDataAdapter.Add(gainLossCompoundModel.Date, useGainLoss ? gainLossCompoundModel.TotalGainLoss : gainLossCompoundModel.TotalGainLossPercent);
      }
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    // This is the least squares composite data source based on the active gain/loss
    public static CompositeDataSource LeastSquares(GainLossCompoundModelCollection gainLossList, bool useGainLoss)
    {
      if (null == gainLossList) return Empty();
      LeastSquaresResult leastSquaresResult = LeastSquaresFit(gainLossList, useGainLoss);
      GainLossCompoundModelCollection sortedCollection = new GainLossCompoundModelCollection(gainLossList.OrderBy(x => x.Date).ToList());
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();

      for (int index = 0; index < sortedCollection.Count; index++)
      {
        GainLossCompoundModel gainLossCompoundModel = sortedCollection[index];
        int leastSquaresIndex = (leastSquaresResult.LeastSquares.Length - 1) - index;
        sortedDateTimeDataAdapter.Add(gainLossCompoundModel.Date, leastSquaresResult.LeastSquares[leastSquaresIndex]);
      }
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    // This is the least squares composite data source based on the active gain/loss
    public static CompositeDataSource TotalLeastSquares(GainLossCompoundModelCollection gainLossList, bool useGainLoss)
    {
      if (null == gainLossList) return Empty();
      LeastSquaresResult leastSquaresResult = TotalLeastSquaresFit(gainLossList, useGainLoss);
      GainLossCompoundModelCollection sortedCollection = new GainLossCompoundModelCollection(gainLossList.OrderBy(x => x.Date).ToList());
      SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();

      for (int index = 0; index < sortedCollection.Count; index++)
      {
        GainLossCompoundModel gainLossCompoundModel = sortedCollection[index];
        int leastSquaresIndex = (leastSquaresResult.LeastSquares.Length - 1) - index;
        sortedDateTimeDataAdapter.Add(gainLossCompoundModel.Date, leastSquaresResult.LeastSquares[leastSquaresIndex]);
      }
      CompositeDataSource compositeDataSource = new CompositeDataSource()
      {
        DataAdapter = sortedDateTimeDataAdapter
      };
      return compositeDataSource;
    }

    // ***************************************** C A L C U L A T O R S **********************************

    // This is the LeastSquares fit based on the active gain/loss
    public static LeastSquaresResult LeastSquaresFit(GainLossCompoundModelCollection gainLossList, bool useGainLoss)
    {
      double[] values = null;
      if (useGainLoss) values = (from GainLossCompoundModel gainLoss in gainLossList select gainLoss.ActiveGainLoss).ToList().ToArray();
      else values = (from GainLossCompoundModel gainLoss in gainLossList select gainLoss.ActiveGainLossPercent).ToList().ToArray();
      LeastSquaresResult leastSquaresResult = Numerics.LeastSquares(values);
      return leastSquaresResult;
    }    
    
// This is the LeastSquares fit based on the total gain/loss
		public static LeastSquaresResult TotalLeastSquaresFit(GainLossCompoundModelCollection gainLossList,bool useGainLoss)
		{
			double[] values=null;
      if(useGainLoss)values=(from GainLossCompoundModel gainLoss in gainLossList select gainLoss.TotalGainLoss).ToList().ToArray();
      else values=(from GainLossCompoundModel gainLoss in gainLossList select gainLoss.TotalGainLossPercent).ToList().ToArray();
			LeastSquaresResult leastSquaresResult=Numerics.LeastSquares(values);
			return leastSquaresResult;
		}    
 }    
}