using DataDisplay.Common;
using DataDisplay.DataSource;
using DataDisplay.Graph;
using DataDisplay.Renderers;
using SkiaSharp;
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData.Utils;
using MarketData.MarketDataModel;

namespace Navigator.Renderers
{
    public class BollingerBandRenderer : IRenderer
    {
      private SKPaint paintK=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=5f};
      private SKPaint paintL=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=5f};
      private SKPaint paintKL1=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=3f};
      private SKPaint paintLP1=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Green),StrokeWidth=3f};
      private SKPaint paintHigh=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Blue),StrokeWidth=4f};                             
      private SKPaint paintLow=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Red),StrokeWidth=4f};
      private SKPaint paintClose=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Black),StrokeWidth=5f};
      private SKPaint paintSMAN=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Purple),StrokeWidth=3f};
      private SKPaint paintPointMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
      private SKPaint paintPointMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.Yellow),StrokeWidth=3f};
      private SKPaint paintParityPricePointMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
      private SKPaint paintParityPricePointMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.DarkBlue),StrokeWidth=3f};
      private SKPaint paintStopLimitMarkerStroke=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
      private SKPaint paintStopLimitMarkerFill=new SKPaint{Style=SKPaintStyle.Fill,Color=new SKColor((uint)SKColors.Red),StrokeWidth=3f};

      public enum Band{K=0,KL1=1,L=2,LP1=3,High=4,Low=5,Close=6,SMAN=7};
      private Dictionary<int,LineGraph> bollingerBandGraphs=new Dictionary<int,LineGraph>();
      private PortfolioTradesWithParityPrice portfolioTradesWithParityPrice;
      private StopLimit stopLimit;
      private DateGenerator dateGenerator=new DateGenerator();
      private bool deviceIsTablet=false;

      public BollingerBandRenderer()
      {
      }

      public void SetPaint(SKPaint paint,Band band)
      {
        if(!bollingerBandGraphs.ContainsKey((int)band))return;
        bollingerBandGraphs[(int)band].SetPaint(paint);
      }

      public bool DeviceIsTablet
      {
        get{return deviceIsTablet;}
        set{deviceIsTablet=value;}
      }

      public StopLimit StopLimit
      {
        get{return stopLimit;}
        set{stopLimit=value;}
      }

      public PortfolioTradesWithParityPrice PortfolioTradesWithParityPrice
      {
        get{return portfolioTradesWithParityPrice;}
        set
        {
          portfolioTradesWithParityPrice=value;
          if(null!=portfolioTradesWithParityPrice)PortfolioTrades=portfolioTradesWithParityPrice.Trades;
        }
      }
      private PortfolioTrades PortfolioTrades{get;set;}

      public CompositeDataSource K
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.K))bollingerBandGraphs[(int)Band.K].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.K,new LineGraph(paintK,value));
        }
      }

      public CompositeDataSource KL1   
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.KL1))bollingerBandGraphs[(int)Band.KL1].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.KL1,new LineGraph(paintKL1,value));
        }
      }

      public CompositeDataSource L   
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.L))bollingerBandGraphs[(int)Band.L].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.L,new LineGraph(paintL,value));
        }
      }

      public CompositeDataSource LP1  
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.LP1,new LineGraph(paintLP1,value));
        }
      }

      public CompositeDataSource High
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.High))bollingerBandGraphs[(int)Band.High].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.High,new LineGraph(paintHigh,value));
        }
      }

      public CompositeDataSource Low
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.Low))bollingerBandGraphs[(int)Band.Low].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.Low,new LineGraph(paintLow,value));
        }
      }

      public CompositeDataSource Close
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.Close))bollingerBandGraphs[(int)Band.Close].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.Close,new LineGraph(paintClose,value));
        }
      }

      private Type GetXDataType()
      {
        return typeof(DateTime);
      }

      private Type GetYDataType()
      {
        return typeof(double);
      }

//*********************************************************************************************************************************************************************
      public CompositeDataSource SMAN
      {
        set
        {
          if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))bollingerBandGraphs[(int)Band.SMAN].SetDataSource(value);
          else bollingerBandGraphs.Add((int)Band.SMAN,new LineGraph(paintSMAN,value));
        }
      }

      public void Refresh()
      {
        RefreshRequested?.Invoke(this, EventArgs.Empty);
      }
// ************************************************************************************************************************************
// *********************************************************  R E N D E R E R *********************************************************
// ************************************************************************************************************************************
      public void PaintSurface(SKSurface surface,SKImageInfo imageInfo)
      {
        SKCanvas canvas = surface.Canvas; 
        canvas.Clear();
        if(!AnyData())return;

        int width=imageInfo.Width;
        int height=imageInfo.Height;

        XDataExtent=GetXDataExtent(); 
        YDataExtent=GetYDataExtent();
        XDataExtentMin=GetXDataExtentMin(); 
        YDataExtentMin=GetYDataExtentMin();

        PointMapping pointMapping=new PointMapping(width,height,XDataExtent,XDataExtentMin,YDataExtent,YDataExtentMin,80,50);

        GridLines gridLines=new GridLines(10,10);
        gridLines.AssignXDataType=GetXDataType;
        gridLines.AssignGetYDataType=GetYDataType;
        gridLines.Render(canvas,pointMapping);

        if(bollingerBandGraphs.ContainsKey((int)Band.K))bollingerBandGraphs[(int)Band.K].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))bollingerBandGraphs[(int)Band.KL1].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.L))bollingerBandGraphs[(int)Band.L].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))bollingerBandGraphs[(int)Band.LP1].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.High))bollingerBandGraphs[(int)Band.High].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))bollingerBandGraphs[(int)Band.Low].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))bollingerBandGraphs[(int)Band.Close].Render(canvas,pointMapping);
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))bollingerBandGraphs[(int)Band.SMAN].Render(canvas,pointMapping);

        RenderPositionPointMarkers(canvas,pointMapping,paintPointMarkerStroke,paintPointMarkerFill);
        RenderParityPricePointMarker(canvas,pointMapping,paintParityPricePointMarkerStroke,paintParityPricePointMarkerFill);
        RenderStopLimitPointMarker(canvas,pointMapping,paintStopLimitMarkerStroke,paintStopLimitMarkerFill);
      }

      public void RenderPositionPointMarkers(SKCanvas canvas,PointMapping pointMapping,SKPaint paintPointMarkerStroke,SKPaint paintPointMarkerFill)
      {
        if(null==PortfolioTrades||0==PortfolioTrades.Count)return;
        DateTime minVisibleDate=new DateTime((long)(pointMapping.XDataExtentMin*10000000000.0));
        DateTime maxVisibleDate=new DateTime((long)(pointMapping.XDataExtent*10000000000.0));

        minVisibleDate=dateGenerator.FindPrevBusinessDay(minVisibleDate); // subtract an extra business day to the extent to handle edge cases
        maxVisibleDate=dateGenerator.FindNextBusinessDay(maxVisibleDate); // add an extra business day to the extent to handle edge cases.
        List<PortfolioTrade> portfolioTrades=PortfolioTrades.Where(x=>x.TradeDate>=minVisibleDate && x.TradeDate<=maxVisibleDate).ToList();
        foreach(PortfolioTrade portfolioTrade in portfolioTrades)
        {
          SKPoint tradePoint=new SKPoint((float)(portfolioTrade.TradeDate.Ticks/10000000000.0),(float)portfolioTrade.Price);
          tradePoint=pointMapping.MapPoint(tradePoint);
          DrawTriangle(canvas,tradePoint,paintPointMarkerStroke,paintPointMarkerFill);
        }
      }

      public void RenderParityPricePointMarker(SKCanvas canvas,PointMapping pointMapping,SKPaint parityPricePointMarkerStroke,SKPaint parityPricePointMarkerFill)
      {
        if(null==PortfolioTradesWithParityPrice||null==PortfolioTradesWithParityPrice.ParityPrice)return;
        SKPoint parityPoint=new SKPoint((float)(portfolioTradesWithParityPrice.ParityPrice.Date.Ticks/10000000000.0),(float)portfolioTradesWithParityPrice.ParityPrice.Close);
        parityPoint=pointMapping.MapPoint(parityPoint);
        DrawTriangle(canvas,parityPoint,parityPricePointMarkerStroke,parityPricePointMarkerFill);
      }

      public void RenderStopLimitPointMarker(SKCanvas canvas,PointMapping pointMapping,SKPaint stopLimitPointMarkerStroke,SKPaint stopLimitPointMarkerFill)
      {
        if(null==stopLimit)return;
        SKPoint stopLimitPoint=new SKPoint((float)(pointMapping.XDataExtent),(float)stopLimit.StopPrice);
        stopLimitPoint=pointMapping.MapPoint(stopLimitPoint);
        DrawTriangle(canvas,stopLimitPoint,stopLimitPointMarkerStroke,stopLimitPointMarkerFill);
      }

      public void DrawTriangle(SKCanvas canvas,SKPoint point,SKPaint paintStrokeMarker,SKPaint paintFillMarker)
      {
        double baseLength = 50;
        if(DeviceIsTablet)baseLength*=.45;
        DrawingHelper.DrawIsoTriangle(canvas, point, (int)baseLength, paintStrokeMarker, paintFillMarker);
      }

      public double XDataExtent{get;set;}

      public double XDataExtentMin{get;set;}

      public double XRange{get{return XDataExtent-XDataExtentMin;}}

      public double YDataExtent{get;set;}

      public double YDataExtentMin{get;set;}

      public double YRange{get{return YDataExtent-YDataExtentMin;}}

      private bool AnyData()
      {
        if(bollingerBandGraphs.ContainsKey((int)Band.K))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.L))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.High))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))return true;
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))return true;
        return false;
      }

      private double GetXDataExtent()
      {
        List<double> xExtents=new List<double>();
        if(bollingerBandGraphs.ContainsKey((int)Band.K))xExtents.Add(bollingerBandGraphs[(int)Band.K].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))xExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.L))xExtents.Add(bollingerBandGraphs[(int)Band.L].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.High))xExtents.Add(bollingerBandGraphs[(int)Band.High].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))xExtents.Add(bollingerBandGraphs[(int)Band.Low].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))xExtents.Add(bollingerBandGraphs[(int)Band.Close].GetXExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))xExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetXExtent());
        return xExtents.Count>0?xExtents.Max(x=>x):0;
      }

      private double GetXDataExtentMin()
      {
        List<double> xExtents=new List<double>();
        if(bollingerBandGraphs.ContainsKey((int)Band.K))xExtents.Add(bollingerBandGraphs[(int)Band.K].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))xExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.L))xExtents.Add(bollingerBandGraphs[(int)Band.L].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))xExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.High))xExtents.Add(bollingerBandGraphs[(int)Band.High].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))xExtents.Add(bollingerBandGraphs[(int)Band.Low].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))xExtents.Add(bollingerBandGraphs[(int)Band.Close].GetXExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))xExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetXExtentMin());
        return xExtents.Count>0?xExtents.Min(x=>x):0;
      }

      private double GetYDataExtent()
      {
        List<double> yExtents=new List<double>();
        if(bollingerBandGraphs.ContainsKey((int)Band.K))yExtents.Add(bollingerBandGraphs[(int)Band.K].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))yExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.L))yExtents.Add(bollingerBandGraphs[(int)Band.L].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.High))yExtents.Add(bollingerBandGraphs[(int)Band.High].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))yExtents.Add(bollingerBandGraphs[(int)Band.Low].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))yExtents.Add(bollingerBandGraphs[(int)Band.Close].GetYExtent());
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))yExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetYExtent());
        double maxDataExtent=yExtents.Count>0?yExtents.Max(x=>x):0;
        if(null!=portfolioTradesWithParityPrice&&null!=portfolioTradesWithParityPrice.ParityPrice&&portfolioTradesWithParityPrice.ParityPrice.Close>maxDataExtent)
        {
          maxDataExtent=portfolioTradesWithParityPrice.ParityPrice.Close;
        }
        return maxDataExtent;
      }

      private double GetYDataExtentMin()
      {
        List<double> yExtents=new List<double>();
        if(bollingerBandGraphs.ContainsKey((int)Band.K))yExtents.Add(bollingerBandGraphs[(int)Band.K].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.KL1))yExtents.Add(bollingerBandGraphs[(int)Band.KL1].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.L))yExtents.Add(bollingerBandGraphs[(int)Band.L].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.LP1))yExtents.Add(bollingerBandGraphs[(int)Band.LP1].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.High))yExtents.Add(bollingerBandGraphs[(int)Band.High].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.Low))yExtents.Add(bollingerBandGraphs[(int)Band.Low].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.Close))yExtents.Add(bollingerBandGraphs[(int)Band.Close].GetYExtentMin());
        if(bollingerBandGraphs.ContainsKey((int)Band.SMAN))yExtents.Add(bollingerBandGraphs[(int)Band.SMAN].GetYExtentMin());
        double minDataExtent=yExtents.Count>0?yExtents.Min(x=>x):0;
        if(null!=portfolioTradesWithParityPrice&&null!=portfolioTradesWithParityPrice.ParityPrice&&portfolioTradesWithParityPrice.ParityPrice.Close<minDataExtent)
        {
          minDataExtent=portfolioTradesWithParityPrice.ParityPrice.Close;
        }
        return minDataExtent;
      }

      public event EventHandler RefreshRequested;
  }
}
