using MarketData.Utils;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace DataDisplay.Common
{
  public class GridLines
  {        
    public delegate Type GetXDataType();
    public delegate Type GetYDataType();
    public delegate String XDataFormatter(Object data);  // custom formatter for XData
    public delegate String YDataFormatter(Object data);  // custom formatter for YData

    private SKPaint paintGrid=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.LightGray),StrokeWidth=1f};
    private SKPaint paintAxis=new SKPaint{Style=SKPaintStyle.Stroke,Color=new SKColor((uint)SKColors.Black),StrokeWidth=3f};
    private SKPaint paintHorizontalLabelText=new SKPaint{TextSize=24f,IsAntialias=true,Color=new SKColor((uint)SKColors.Black),IsStroke=false,Typeface=SKTypeface.FromFamilyName("Calibri",SKFontStyleWeight.Bold,SKFontStyleWidth.Normal,SKFontStyleSlant.Upright)};    // dates
    private SKPaint paintVerticalLabelText=new SKPaint{TextSize=24f,IsAntialias=true,Color=new SKColor((uint)SKColors.Black),IsStroke=false,Typeface=SKTypeface.FromFamilyName("Calibri",SKFontStyleWeight.Bold,SKFontStyleWidth.Normal,SKFontStyleSlant.Upright)};      // values
    private GetXDataType getXDataType=null;
    private GetYDataType getYDataType=null;
    private XDataFormatter xDataFormatter;
    private YDataFormatter yDataFormatter;
    private Dictionary<String,String> uniqueVerticalLabels;
    private Dictionary<String,String> uniqueHorizontalLabels;

    public GridLines(double xGridLines,double yGridLines)
    {
      XGridLines=xGridLines;
      YGridLines=yGridLines;
      uniqueVerticalLabels=new Dictionary<String,String>();
      uniqueHorizontalLabels=new Dictionary<String,String>();
    }
    public double XGridLines{get;private set;}
    public double YGridLines{get;private set;}

    public GetXDataType AssignXDataType
    {
      get{return getXDataType;}
      set{getXDataType=value;}
    }

    public GetYDataType AssignGetYDataType
    {
      get{return getYDataType;}
      set{getYDataType=value;}
    }

    public XDataFormatter AssignXDataFormatter
    {
      get{return xDataFormatter;}
      set{xDataFormatter=value;}
    }

    public YDataFormatter AssignYDataFormatter
    {
      get{return yDataFormatter;}
      set{yDataFormatter=value;}
    }

    public void Render(SKCanvas canvas,PointMapping pointMapping,SKPaint axisPaint=null,SKPaint gridPaint=null)
    {
      if(null==axisPaint)axisPaint=paintAxis;
      if(null==gridPaint)gridPaint=paintGrid;
      SKPoint p1;
      SKPoint p2;

      double xLineFactor = pointMapping.XRange/XGridLines;
      double yLineFactor = pointMapping.YRange/YGridLines;

      if(0==xLineFactor)xLineFactor=.01;
      if(0==yLineFactor)yLineFactor=.01;

// Draw horizontal line grid - the labels go along the left
      for (double yIndex = pointMapping.YDataExtentMin; yIndex <= pointMapping.YDataExtent+.05; yIndex += yLineFactor)
      {
        p1 = pointMapping.MapPoint(new SKPoint((float)pointMapping.XDataExtentMin, (float)yIndex));
        p2 = pointMapping.MapPoint(new SKPoint((float)pointMapping.XDataExtent, (float)yIndex));
        if(yIndex==pointMapping.YDataExtentMin)canvas.DrawPoints(SKPointMode.Polygon, new SKPoint[] { p1, p2 }, axisPaint);
        else canvas.DrawPoints(SKPointMode.Polygon, new SKPoint[] { p1, p2 }, gridPaint);
        p1.X=0f;
        DisplayXAxisLabel(canvas,pointMapping,p1,yIndex);
      }
// Draw vertical line grid - the labels go along the bottom
      for (double xIndex = pointMapping.XDataExtentMin; xIndex <= pointMapping.XDataExtent+.05; xIndex += xLineFactor)
      {
        p1 = pointMapping.MapPoint(new SKPoint((float)xIndex,(float)pointMapping.YDataExtentMin));
        p2 = pointMapping.MapPoint(new SKPoint((float)xIndex, (float)pointMapping.YDataExtent));
        if(xIndex==pointMapping.XDataExtentMin)canvas.DrawPoints(SKPointMode.Polygon, new SKPoint[] { p1, p2 }, axisPaint);
        else canvas.DrawPoints(SKPointMode.Polygon, new SKPoint[] { p1, p2 }, gridPaint);
        p1.Y=(float)pointMapping.Height;
        DisplayYAxisLabel(canvas,pointMapping,p1,xIndex);
      }
    }
//  The labels along the left of the chart
    private void DisplayXAxisLabel(SKCanvas canvas,PointMapping pointMapping,SKPoint p1,double yIndex)
    {
      String strLabel;
      double percentage=(yIndex - pointMapping.YDataExtentMin) / pointMapping.YRange;
      Type dataType=typeof(double);
      if(null!=getYDataType)dataType=getYDataType();
      if(dataType.Equals(typeof(DateTime)))
      {
        DateTime axisDate=new DateTime((long)((pointMapping.YDataExtentMin+(pointMapping.YRange*percentage))*10000000000.0));
        if(null!=yDataFormatter)strLabel=yDataFormatter(axisDate);
        else strLabel=Utility.DateTimeToStringMMSYY(axisDate);
      }
      else if(dataType.Equals(typeof(double)))
      {
        double value=pointMapping.YDataExtentMin+(pointMapping.YRange*percentage);
        if(null!=yDataFormatter)strLabel=yDataFormatter(value);
        else 
        {
          if(value>=1000)strLabel=Utility.FormatNumber(value,0,true);
          else strLabel=Utility.FormatNumber(value,2,true);
        }
      }
      else 
      {
        double value=pointMapping.YDataExtentMin+(pointMapping.YRange*percentage);
        strLabel=Utility.FormatNumber(value,0,true);
      }
      if(uniqueVerticalLabels.ContainsKey(strLabel))return;
      uniqueVerticalLabels.Add(strLabel,strLabel);
      if(null!=strLabel)canvas.DrawText(strLabel, p1, paintVerticalLabelText);
    }
// The labels along the bottom of the chart
    private void DisplayYAxisLabel(SKCanvas canvas,PointMapping pointMapping,SKPoint p1,double xIndex)
    {
      String strLabel=null;
      double percentage=(xIndex-pointMapping.XDataExtentMin)/pointMapping.XRange;
      Type dataType=typeof(double);
      if(null!=getXDataType())dataType=getXDataType();

      if (dataType.Equals(typeof(DateTime)))
      {
        DateTime axisDate=new DateTime((long)((pointMapping.XDataExtentMin+(pointMapping.XRange*percentage))*10000000000.0));
        if(null!=xDataFormatter)strLabel=xDataFormatter(axisDate);
        else strLabel=Utility.DateTimeToStringMMSYY(axisDate);
      }
      else if(dataType.Equals(typeof(double)))
      {
        double value=pointMapping.XDataExtentMin+(pointMapping.XRange*percentage);
        if(null!=xDataFormatter)strLabel=xDataFormatter(value);
        else
        {
          if(value>=1000)strLabel=Utility.FormatNumber(value,0,true);
          else strLabel=Utility.FormatNumber(value,2,true);
        }
      }
      else
      {
        double value=pointMapping.XDataExtentMin+(pointMapping.XRange*percentage);
        strLabel=Utility.FormatNumber(value,0,true);
      }
      if(uniqueHorizontalLabels.ContainsKey(strLabel))return;
      uniqueHorizontalLabels.Add(strLabel,strLabel);
      if(null!=strLabel)canvas.DrawText(strLabel, p1, paintHorizontalLabelText);
    }
  }
}
