import { BaseRenderer } from './base-renderer';
import { select as d3Select, create as d3Create, line as d3Line, axisBottom, axisLeft, arc as d3Arc, } from '../../../polyfill/d3';
/**  This class is used to render charts using SVG elements. */
export class SVGRenderer extends BaseRenderer {
  /**
   * The function sets the layout of a chart using D3 and SVG.
   *
   * @param {number} width - chart width
   * @param {number} height - chart height
   * @param {number} top - chart margin top
   * @param {number} left - chart margin left
   */
  setLayout(width, height, top, left) {
    d3Select(this.context)
      .attr('width', width)
      .attr('height', height)
      .attr('viewBox', [0, 0, width, height])
      .property('style', 'display:block;max-width:100%;height:auto;')
      .call((g) => {
      g.append('g')
        .attr('class', 'vega-chart-draw-g')
        .attr('transform', `translate(${left},${top})`);
    })
      .call((g) => {
      g.append('g')
        .attr('class', 'vega-chart-legend-g')
        .attr('transform', `translate(${left / 3},${top / 4})`);
    });
  }
  /**
   * This function draws a line on a chart using D3.js library.
   *
   * @param {ChartPosition} from - The starting position of the line to be drawn on the chart. It is an
   * object with two properties: x and y, representing the x and y coordinates of the starting point respectively.
   * @param {ChartPosition} to - The "to" parameter is a ChartPosition object that represents the ending
   * point of the line to be drawn. It contains the x and y coordinates of the point on the chart where the line should end.
   * @param {string} color - The color of the line
   */
  drawLine(from, to, color) {
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('line')
      .attr('x1', from.x)
      .attr('y1', from.y)
      .attr('x2', to.x)
      .attr('y2', to.y)
      .attr('stroke', color);
  }
  /**
   * This function draws an x-axis on a chart using D3 and a given scale and end point.
   *
   * @param {ChartExtend<unknown>} extend - which is an object that contains information about the chart's scale and end position on the y-axis. The `scale` property
   * is a D3 scale object that maps data values to pixel positions on the x-axis
   * @param {string} color - The stroke color of axis
   * @param {string} textStyle - The text style of the axis
   */
  drawXAxis(extend, color, textStyle) {
    const { scale, end, labelFormatter, direction, height } = extend;
    let axis = axisBottom(scale);
    if (labelFormatter) {
      axis = axis.tickFormat(labelFormatter);
    }
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('g')
      .attr('class', 'vega-chart-x-axis')
      .attr('transform', `translate(0, ${end.y})`)
      .call(axis)
      .call((g) => {
      g.select('.domain').attr('stroke-width', 2).attr('stroke', color);
    })
      .call((g) => {
      g.selectAll('line').attr('stroke', color);
      g.selectAll('text').property('style', textStyle);
    })
      .call((g) => {
      if (direction === 'horizontal') {
        g.selectAll('line').clone().attr('y2', `${-height}`);
        g.select('.domain').attr('stroke-width', 0);
      }
    });
  }
  /**
   * This function draws the Y-axis of a chart using D3 and a given scale.
   *
   * @param {ChartExtend<unknown>} extend -  It likely contains additional information or settings related to the chart being drawn, but without more context it's
   * difficult to say exactly what it contains.
   * @param {string} color - The stroke color of the axis
   * @param {string} textStyle - The text style of the axis
   */
  drawYAxis(extend, color, textStyle) {
    const { scale, labelFormatter, width, tickSize, direction } = extend;
    let axis = axisLeft(scale).ticks(tickSize);
    if (labelFormatter) {
      axis = axis.tickFormat(labelFormatter);
    }
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('g')
      .attr('class', 'vega-chart-y-axis')
      .call(axis)
      .call((g) => {
      g.select('.domain').attr('stroke-width', 0).attr('stroke', color);
    })
      .call((g) => {
      g.selectAll('line').attr('stroke-width', 2).attr('stroke', color);
      g.selectAll('text').property('style', textStyle);
    })
      .call((g) => {
      if (direction !== 'horizontal') {
        g.selectAll('line').clone().attr('x2', `${width}`);
      }
      else {
        g.select('.domain').attr('stroke-width', 2).attr('stroke', color);
      }
    });
  }
  /**
   * This function draws a circle with a given center, radius, and color using D3.js.
   *
   * @param {ChartPosition} center - ChartPosition, which is an object containing the x and y coordinates of the center of the circle.
   * @param {number} radius - The radius parameter is a number that specifies the radius of the circle to be drawn.
   * @param {string} fillColor - The color parameter is a string that represents the color of the circle to be drawn
   * @param {number} strokeWidth - the border width
   * @param {string} strokeColor - the border color
   */
  drawCircle(center, radius, fillColor, strokeWidth, strokeColor) {
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append(() => this.getCircleNode(center, radius, fillColor, strokeWidth, strokeColor));
  }
  /**
   * This function draws text on a chart at a specified position using D3.js.
   *
   * @param {ChartPosition} position - ChartPosition is an object that contains the x and y coordinates where the text will be positioned on the chart.
   * @param {string} text - The text parameter is a string that represents the text that will be
   * displayed on the chart. It is the content that will be added to the text element created by this function.
   * @param {string} textStyle - The text style of the axis
   * @param {TextAnchorType} textAnchor - text anchor
   * @param {number} rotate - rotate of the text
   */
  drawText(position, text, textStyle, textAnchor, rotate) {
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append(() => this.getTextNode(position, text, textStyle, textAnchor, rotate));
  }
  /**
   * This function draws a legend item with a colored circle and text at a specified position on a chart.
   *
   * @param {ChartPosition} position - The position parameter is an object that contains the x and y
   * coordinates where the legend should be placed on the chart.
   * @param {string} text - The text to be displayed in the legend.
   * @param {string} color - Background color token type, which is a type of color
   * used in the chart's background.
   * @param {string} textStyle - The text style of the axis
   * @returns {SVGGElement} svg g element
   */
  drawLegend(position, text, color, textStyle) {
    return d3Select(this.context)
      .select('.vega-chart-legend-g')
      .append('g')
      .attr('class', 'vega-chart-legend-item')
      .attr('data-legend', text)
      .attr('transform', `translate(${position.x},${position.y})`)
      .call((g) => {
      g.append(() => this.getCircleNode({
        x: -4,
        y: -4,
      }, 4, color));
      g.append(() => this.getTextNode({ x: 8, y: 0 }, text, textStyle));
    })
      .node();
  }
  /**
   * This function draws a rectangle with a specified position, width, height, and optional color using D3.js.
   *
   * @param {ChartPosition} position - The position parameter is an object that contains the x and y coordinates of the top-left corner of the rectangle.
   * @param {number} width - The width of the rectangle to be drawn.
   * @param {number} height - The height parameter is a number that represents the height of the rectangle to be drawn.
   * @param {string} [color] - The color parameter is an optional string that represents the color of the rectangle to be drawn. If it is not provided, the rectangle will have no fill color.
   */
  drawRect(position, width, height, color) {
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('rect')
      .attr('fill', color)
      .attr('x', position.x)
      .attr('y', position.y)
      .attr('width', width)
      .attr('height', height);
  }
  /**
   * This function draws a path on a chart using D3 and SVG based on the provided positions and color.
   *
   * @param {ChartPosition[]} positions - An array of ChartPosition objects that represent the
   * coordinates of the points on the path to be drawn. Each ChartPosition object contains an x and y
   * value that represent the position of the point on the chart.
   * @param {string} color - line color
   * represents a color value. It is likely used to specify the color of the path that will be drawn on the chart.
   */
  drawLinePath(positions, color) {
    d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('path')
      .attr('class', 'vega-chart-line-path')
      .datum(positions)
      .attr('fill', 'none')
      .attr('stroke', color)
      .attr('stroke-width', 3)
      .attr('stroke-linecap', 'round')
      .attr('stroke-linejoin', 'round')
      .attr('d', d3Line()
      .x((position) => position.x)
      .y((position) => position.y));
  }
  /**
   * This function draws an arc path using D3 library and applies color and radius based on input parameters.
   *
   * @param {ChartPosition} center - the arc center position
   * @param {ArcPathItem} record - an object containing data for the arc path to be drawn
   * @param {string} color - The color parameter
   * and is used to set the fill color of the arc path.
   * @param {number} outerRadius - The outer radius parameter is a number that determines the outer
   * radius of the arc path being drawn.
   * @param {number} innerRadius - The inner radius of the arc path, specified as a number.
   * @returns {SVGPathElement} svg arc path element.
   */
  drawArcPath(center, record, color, outerRadius, innerRadius) {
    return d3Select(this.context)
      .select('.vega-chart-draw-g')
      .append('path')
      .datum(record)
      .attr('class', 'vega-chart-arc-path')
      .attr('data-name', (item) => item.data.name)
      .attr('transform', `translate(${center.x},${center.y})`)
      .attr('fill', color)
      .attr('stroke-width', 0)
      .attr('d', d3Arc().innerRadius(innerRadius).outerRadius(outerRadius))
      .attr('data-d', d3Arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius + 10))
      .node();
  }
  /**
   * This function clears all elements within two specific classes in a D3 selection.
   */
  clear() {
    d3Select(this.context).selectAll('.vega-chart-draw-g').remove();
    d3Select(this.context).selectAll('.vega-chart-legend-g').remove();
  }
  /**
   * This TypeScript function creates and returns an SVG text element with the given position and text.
   *
   * @param {ChartPosition} position - The position parameter is an object that contains the x and y
   * coordinates where the text element will be placed on the SVG canvas.
   * @param {string} text - The text parameter is a string that represents the text content that will be
   * displayed in the SVGTextElement.
   * @param {string} textStyle - The text style of the axis
   * @param {TextAnchorType} textAnchor - text anchor
   * @param {number} rotate - rotate of the text
   * @returns {SVGTextElement} an SVGTextElement node.
   */
  getTextNode(position, text, textStyle, textAnchor = 'start', rotate = 0) {
    const TextSelectionType = d3Create('svg:text')
      .property('style', textStyle)
      .attr('x', position.x)
      .attr('y', position.y)
      .attr('text-anchor', textAnchor)
      .attr('rotate', rotate)
      .text(text);
    return TextSelectionType.node();
  }
  /**
   * This TypeScript function creates an SVG circle element with a specified center, radius, and color.
   *
   * @param {ChartPosition} center - The center position of the circle on the chart, specified as an
   * object with `x` and `y` properties.
   * @param {number} radius - The radius parameter is a number that represents the radius of the circle
   * to be created.
   * @param {string} fillColor - The color parameter
   * and is used to determine the fill color of the circle. It is likely an enum or string type that maps
   * to a specific color value.
   * @param {number} strokeWidth - the border width
   * @param {string} strokeColor - the border color
   * @returns {SVGCircleElement} an SVG circle element.
   */
  getCircleNode(center, radius, fillColor, strokeWidth, strokeColor) {
    const circleSelection = d3Create('svg:circle')
      .attr('r', radius)
      .attr('cx', center.x)
      .attr('cy', center.y)
      .attr('fill', fillColor)
      .attr('stroke-width', strokeWidth || 0)
      .attr('stroke', strokeColor);
    return circleSelection.node();
  }
}
