import React, { useState, useRef, useMemo, useEffect, memo } from 'react';
import { scaleLinear } from 'd3-scale';
import { select } from 'd3-selection';
import { lineRadial, curveCardinalClosed } from 'd3-shape';
import { dataHooks } from './constants';
import { st, classes } from './RadarChart.st.css.js';
import { stVars as colorsStVars } from '../Foundation/stylable/colors.st.css.js';
import Tooltip from '../Tooltip';
const BLUE_COLOR = '#3899ec';
const DISABLED_COLOR = '#879cae';
const DOT_RADIUS = 2;
const LINE_DY = 0.35;
const LINE_HEIGHT = 1.4;
const PADDING = 12;
const AXIS_LABEL_DY = 12;
const getXY = (value, index, angle, rScale, shift = 0) => ({
    x: (rScale(value) + shift) * Math.cos(angle * index - Math.PI / 2),
    y: (rScale(value) + shift) * Math.sin(angle * index - Math.PI / 2),
});
const CirclesSVG = memo(({ scale, rScale }) => {
    return scale
        .map(({ value }, index) => (React.createElement("circle", { key: index, "data-hook": dataHooks.radarChartScaleLine, className: st(classes.circles, {
            isTransparent: index !== scale.length - 1,
        }), r: rScale(value), cx: 0, cy: 0 })))
        .reverse();
});
const WebChartSVG = memo(({ disabled, dots }) => {
    if (disabled || dots.length === 0) {
        return null;
    }
    return (React.createElement("defs", null,
        React.createElement("linearGradient", { id: "gradient", x1: "0", x2: "1", y1: "0", y2: "0" },
            React.createElement("stop", { offset: "0", stopColor: dots[dots.length - 1].color }),
            React.createElement("stop", { offset: "1", stopColor: dots[0].color }))));
});
const OuterLabels = memo(({ data, maxScaleValue, angle, rScale, labelDistance, disabled, internalHoverIndex, width, setInternalHoverIndex, }) => {
    const outerCircleDots = data.map(({ label }, index) => ({
        label,
        labelPoint: getXY(maxScaleValue, index, angle, rScale, labelDistance),
        linePoint: getXY(maxScaleValue, index, angle, rScale),
    }));
    return outerCircleDots.map(({ label, labelPoint, linePoint }, index) => (React.createElement("g", { key: index, "data-hook": dataHooks.radarChartDataItem },
        React.createElement("line", { className: classes.axis, x1: 0, y1: 0, x2: linePoint.x, y2: linePoint.y }),
        React.createElement("text", { className: st(classes.axisLabel, {
                hovered: index === internalHoverIndex,
                size: width < 360 ? 'small' : 'large',
                disabled,
            }), "data-hook": `axisName${index}`, x: labelPoint.x, y: labelPoint.y, dy: `${LINE_DY}em`, textAnchor: "middle", onMouseEnter: () => !disabled && setInternalHoverIndex(index), onMouseLeave: () => !disabled && setInternalHoverIndex(null) }, label))));
});
const ScaleValues = memo(({ scale, rScale }) => {
    const scaleValues = scale.map(({ value, label }) => ({
        value,
        label,
        ...getXY(value, 0, 0, rScale),
    }));
    return scaleValues.map(({ label, y }, index) => (React.createElement("text", { key: index, className: classes.scaleLabel, x: 0, y: y, dy: AXIS_LABEL_DY, textAnchor: "middle" }, label)));
});
const DataPoints = memo(({ dots, disabled, internalHoverIndex, setInternalHoverIndex }) => dots.map((dot, index) => (React.createElement("g", { key: index },
    React.createElement("circle", { r: !disabled && index === internalHoverIndex
            ? DOT_RADIUS * 2
            : DOT_RADIUS, cx: dot.x, cy: dot.y, fill: disabled ? DISABLED_COLOR : dots[index].color }),
    React.createElement("circle", { className: st(classes.dataPoint, { disabled }), r: DOT_RADIUS * 4, cx: dot.x, cy: dot.y, fillOpacity: 0, onMouseEnter: () => !disabled && setInternalHoverIndex(index), onMouseLeave: () => !disabled && setInternalHoverIndex(null), ...(!disabled && {
            focusable: true,
            tabIndex: 0,
            onFocus: () => !disabled && setInternalHoverIndex(index),
            onBlur: () => !disabled && setInternalHoverIndex(null),
        }) })))));
const DataPointsTooltips = memo(({ dots, containerWidth, internalHoverIndex, disabled }) => dots.map(({ x, y, tooltipContent, value }, index) => (React.createElement("div", { key: index, style: {
        position: 'absolute',
        left: x + containerWidth / 2,
        top: y + containerWidth / 2 - 7,
    } },
    React.createElement(Tooltip, { className: classes.tooltip, shown: index === internalHoverIndex, content: tooltipContent ?? value, disabled: disabled },
        React.createElement("div", null))))));
const WebChartLine = memo(({ disabled, data, angle, rScale }) => {
    const ref = useRef(null);
    useEffect(() => {
        if (ref.current && data.length) {
            select(ref.current).selectAll("[data-hook='curvePart']").remove();
            const drawCurve = lineRadial()
                .curve(curveCardinalClosed)
                .radius(d => rScale(d.value))
                .angle((_d, i) => i * angle);
            const blob = select(ref.current)
                .selectAll("[data-hook='curvePart']")
                .data([data])
                .enter()
                .append('g')
                .attr('data-hook', 'curvePart');
            blob
                .append('path')
                .attr('d', d => drawCurve(d))
                .style('stroke', BLUE_COLOR)
                .style('stroke-width', '1px')
                .style('stroke', disabled ? DISABLED_COLOR : 'url(#gradient)')
                .style('fill', disabled ? DISABLED_COLOR : 'url(#gradient)')
                .style('stroke-dasharray', disabled ? 6 : null)
                .style('fill-opacity', 0.3);
        }
    }, [ref, data, disabled, rScale, angle]);
    return React.createElement("g", { ref: ref });
});
const RadarChart = ({ dataHook, data = [], scale = [
    { value: 50, label: '50%' },
    { value: 100, label: '100%' },
], disabled, width = 150, labelDistance = 50, hoverIndex, labelWidth = 100, onDataPointHover = () => { }, }) => {
    const [internalHoverIndex, setInternalHoverIndex] = useState(hoverIndex);
    const containerWidth = useMemo(() => width + labelDistance * 4 + PADDING * 2, [width, labelDistance]);
    const maxScaleValue = useMemo(() => Math.max(...scale.map(({ value }) => value)), [scale]);
    const rScale = useMemo(() => scaleLinear()
        .range([0, width / 2])
        .domain([0, maxScaleValue]), [width, maxScaleValue]);
    const angle = useMemo(() => (Math.PI * 2) / data.length, [data]);
    const dots = useMemo(() => data.map(({ value, label, color, tooltipContent }, index) => ({
        value,
        label,
        color: colorsStVars[color] || color || colorsStVars['A1'],
        tooltipContent,
        ...getXY(value, index, angle, rScale),
    })), [data, angle, rScale]);
    useEffect(() => setInternalHoverIndex(hoverIndex), [hoverIndex]);
    useEffect(() => {
        if (internalHoverIndex === null) {
            return;
        }
        onDataPointHover(data[internalHoverIndex], internalHoverIndex);
    }, [internalHoverIndex, onDataPointHover, data]);
    useEffect(() => {
        data.forEach(({ label }, index) => {
            const text = select(containerRef.current).select(`[data-hook='axisName${index}']`);
            const words = label.split(/\s+/);
            let word;
            let line = [];
            let lineNumber = 0;
            let tspan = text
                .text(null)
                .append('tspan')
                .attr('x', text.attr('x'))
                .attr('y', text.attr('y'))
                .attr('dy', `${LINE_DY}em`);
            while (words.length) {
                word = words.shift();
                line.push(word);
                tspan.text(line.join(' '));
                if (tspan.node &&
                    tspan.node().getComputedTextLength &&
                    tspan.node().getComputedTextLength() > labelWidth) {
                    line.pop();
                    tspan.text(line.join(' '));
                    line = [word];
                    tspan = text
                        .append('tspan')
                        .attr('x', text.attr('x'))
                        .attr('y', text.attr('y'))
                        .attr('dy', `${++lineNumber * LINE_HEIGHT + LINE_DY}em`)
                        .text(word);
                }
            }
        });
    });
    const containerRef = useRef(null);
    return (React.createElement("div", { "data-hook": dataHook, className: classes.root, style: { width: containerWidth, height: containerWidth }, "data-disabled": disabled, "data-hover-index": internalHoverIndex },
        React.createElement("svg", { width: containerWidth, height: containerWidth, ref: containerRef },
            React.createElement("g", { transform: `translate(${containerWidth / 2}, ${containerWidth / 2})` },
                React.createElement(WebChartSVG, { dots: dots, disabled: disabled }),
                React.createElement(CirclesSVG, { scale: scale, rScale: rScale }),
                React.createElement(OuterLabels, { data: data, maxScaleValue: maxScaleValue, angle: angle, rScale: rScale, labelDistance: labelDistance, disabled: disabled, internalHoverIndex: internalHoverIndex, width: width, setInternalHoverIndex: setInternalHoverIndex }),
                React.createElement(ScaleValues, { scale: scale, rScale: rScale }),
                React.createElement(WebChartLine, { disabled: disabled, data: data, angle: angle, rScale: rScale }),
                React.createElement(DataPoints, { dots: dots, disabled: disabled, internalHoverIndex: internalHoverIndex, setInternalHoverIndex: setInternalHoverIndex }))),
        React.createElement(DataPointsTooltips, { dots: dots, disabled: disabled, containerWidth: containerWidth, internalHoverIndex: internalHoverIndex, setInternalHoverIndex: setInternalHoverIndex })));
};
RadarChart.displayName = 'RadarChart';
export default RadarChart;
//# sourceMappingURL=RadarChart.js.map