using SkiaSharp;

namespace MarketData.CNNProcessing
{
  /// <summary>
  /// This class provides functionality to both scale and map data points from an upper left coordinate system to a lower left coordinate system
  /// </summary>
  public class PointMapping
  {
    
    public PointMapping(double width, double height, double xDataExtent, double xDataExtentMin, double yDataExtent, double yDataExtentMin, double xMarginExtent = 0.00, double yMarginExtent = 0.00)
    {
      Width = width;
      Height = height;
      XMargin = xMarginExtent;
      YMargin = yMarginExtent;
      XDataExtent = xDataExtent;
      XDataExtentMin = xDataExtentMin;
      YDataExtent = yDataExtent;
      YDataExtentMin = yDataExtentMin;
      XScalingFactor = (Width - (XMargin * 2.00)) / (XDataExtent - XDataExtentMin);
      YScalingFactor = (Height - (YMargin * 2.00)) / (YDataExtent - YDataExtentMin);
    }

    public PointMapping(PointMapping pointMapping)
    {
      Width = pointMapping.Width;
      Height = pointMapping.Height;
      XMargin = pointMapping.XMargin;
      YMargin = pointMapping.YMargin;
      XDataExtent = pointMapping.XDataExtent;
      YDataExtent = pointMapping.YDataExtent;
      XScalingFactor = pointMapping.XScalingFactor;
      YScalingFactor = pointMapping.YScalingFactor;
    }

    /// <summary>
    /// MapPoint will both scale the given point and translate the given point from an upper left origin system to a bottom left origin system
    /// Because of the scaling factor (XScalingFactor and YScalingFactor) it is possible for the resulting X,Y values to be rounded out of range.
    /// To remedy this we perform a boundary check of the X,Y points against the extents and set them to those extents if they are out of range.
    /// </summary>
    /// <param name="sourcePoint"></param>
    /// <returns></returns>
    public SKPoint MapPoint(SKPoint sourcePoint)
    {
      SKPoint mappedPoint = new SKPoint((int)((sourcePoint.X - XDataExtentMin) * XScalingFactor), (int)((Height - 1) - ((sourcePoint.Y - YDataExtentMin) * YScalingFactor)));
      mappedPoint.X += (int)XMargin;   // offset by the xMargin
      mappedPoint.Y -= (int)YMargin;   // offset by the yMargin
      if (mappedPoint.X > XDataExtent) mappedPoint.X = (int)XDataExtent;  // constrain X to XDataExtent
      if (mappedPoint.Y > YDataExtent) mappedPoint.Y = (int)YDataExtent;  // constrain Y to YDataExtent
      if (mappedPoint.X < XDataExtentMin) mappedPoint.X = (int)XDataExtentMin; // constrain X to XDataExtentMin
      if (mappedPoint.Y < YDataExtentMin) mappedPoint.Y = (int)YDataExtentMin; // constrain Y to YDataExtentMin
      return mappedPoint;
    }
// TranslatePoint will only translate the given point from an upper left origin system to a bottom left origin system
    public SKPoint TranslatePoint(SKPoint sourcePoint)
    {
      SKPoint mappedPoint=new SKPoint((int)sourcePoint.X,(int)(Height-sourcePoint.Y-1));
      return mappedPoint;
    }
    
    public double Width { get; private set; }
    public double Height{get;private set;}
    public double XDataExtent{get;private set;}
    public double XDataExtentMin{get;private set;}
    public double XRange{get{return XDataExtent-XDataExtentMin;}}
    public double YDataExtent{get;private set;}
    public double YDataExtentMin{get;private set;}
    public double YRange{get{return YDataExtent-YDataExtentMin;}}
    public double XMargin{get;private set;}
    public double YMargin{get;private set;}
    public double XScalingFactor{get;private set;}
    public double YScalingFactor{get;private set;}
  }
}
