import { useEffect, useState } from "react";
import { axisBottom, axisLeft, scaleLinear, select, stack, scaleOrdinal, area, line, } from "d3";
import { DEFAULT_EXTENTS } from "./constants";
import { getExtents, isVoid, isNotVoid, getXAxisShift, drawFocus, getClosest, showFocus, hideFocus, getTooltipClass, showTooltip, hideTooltip, } from "./utils";
export const useD3StakedChart = ({ svgRef, title, serie, height, width, margin, ticksX, formatterX, formatterYLeft, }) => {
    const [svg, setSvg] = useState();
    const [svgPath, setSvgPath] = useState();
    const [stackedPositive, setStackedPositive] = useState([]);
    const [stackedNegative, setStackedNegative] = useState([]);
    const [positiveExtents, setPositiveExtents] = useState(DEFAULT_EXTENTS);
    const [negativeExtents, setNegativeExtents] = useState(DEFAULT_EXTENTS);
    const [extents, setExtents] = useState(DEFAULT_EXTENTS);
    const [totals, setTotals] = useState([]);
    const [hasListeners, setHasListeners] = useState(false);
    const [positiveDrawn, setPositiveDrawn] = useState(false);
    const [negativeDrawn, setNegativeDrawn] = useState(false);
    // initial selection
    useEffect(() => {
        if (!svg && svgRef && svgRef.current) {
            const svgNext = select(svgRef.current);
            setSvg(svgNext);
            const svgPathNext = svgNext.selectAll("path");
            setSvgPath(svgPathNext);
        }
    }, [svgRef.current, svg]);
    // calc stacked data
    useEffect(() => {
        const { keysPositive, keysNegative, data } = serie;
        if (data.length > 0 && keysPositive.length > 0) {
            const yData = data.map(({ x, y }) => ({ ...y, ...{ x } }));
            const stackedPositiveNext = stack().keys(keysPositive)(yData);
            setStackedPositive(stackedPositiveNext);
        }
        if (data.length > 0 && keysNegative.length > 0) {
            const yData = data.map(({ x, y }) => ({ ...y, ...{ x } }));
            const stackedNegativeNext = stack().keys(keysNegative)(yData);
            setStackedNegative(stackedNegativeNext);
        }
    }, [serie]);
    // calc average
    useEffect(() => {
        if (stackedPositive.length === 0 || stackedNegative.length === 0)
            return;
        const positives = stackedPositive[stackedPositive.length - 1];
        const negatives = stackedNegative[stackedNegative.length - 1];
        if (positives.length !== negatives.length)
            return;
        const { length } = positives;
        const totalsNext = [];
        for (let i = 0; i < length; i++) {
            const x = positives[i].data.x;
            const y = positives[i][1] + negatives[i][1];
            totalsNext.push([x, y]);
        }
        setTotals(totalsNext);
    }, [stackedPositive, stackedNegative]);
    // calc extents
    useEffect(() => {
        const { data } = serie;
        if (data.length > 0 && stackedPositive.length > 0) {
            const extentsNext = getExtents(data, stackedPositive);
            setPositiveExtents(extentsNext);
        }
    }, [serie, stackedPositive]);
    useEffect(() => {
        const { data } = serie;
        if (data.length > 0 && stackedNegative.length > 0) {
            const extentsNext = getExtents(data, stackedNegative);
            setNegativeExtents(extentsNext);
        }
    }, [serie, stackedNegative]);
    useEffect(() => {
        const extentsNext = { ...DEFAULT_EXTENTS };
        const p = positiveExtents;
        const n = negativeExtents;
        const minX = [p.minX, n.minX].filter(isNotVoid);
        const minY = [p.minY, n.minY].filter(isNotVoid);
        const maxX = [p.maxX, n.maxX].filter(isNotVoid);
        const maxY = [p.maxY, n.maxY].filter(isNotVoid);
        if (minX.length === 0 ||
            minY.length === 0 ||
            maxX.length === 0 ||
            maxY.length === 0)
            return;
        extentsNext.minX = Math.min(...minX);
        extentsNext.minY = Math.min(...minY);
        extentsNext.maxX = Math.max(...maxX);
        extentsNext.maxY = Math.max(...maxY);
        setExtents(extentsNext);
    }, [positiveExtents, negativeExtents]);
    // draw positive chart
    useEffect(() => {
        if (!svgPath ||
            serie.data.length === 0 ||
            stackedPositive.length === 0 ||
            positiveDrawn)
            return;
        const { minX, minY, maxX, maxY } = positiveExtents;
        if (minX === undefined ||
            minY === undefined ||
            maxX === undefined ||
            maxY === undefined)
            return;
        const { keysPositive, colorsPositive } = serie;
        const colorScalePositive = scaleOrdinal()
            .domain(keysPositive)
            .range(colorsPositive);
        const xScale = scaleLinear()
            .domain([minX, maxX])
            .range([margin.left, width - margin.right]);
        const xAxisShift = getXAxisShift({
            height,
            margin,
            extents,
            positiveExtents,
        });
        if (xAxisShift === undefined)
            return;
        const yScalePositive = scaleLinear()
            .domain([minY, maxY])
            .range([xAxisShift, margin.top]);
        //@ts-ignore
        svgPath
            .remove()
            .data(stackedPositive)
            .enter()
            .append("path")
            .style("fill", (d) => colorScalePositive(colorsPositive[d.index]))
            .attr("d", 
        //@ts-ignore
        area()
            //@ts-ignore
            .x((d) => xScale(d.data.x))
            .y0((d) => yScalePositive(d[0]))
            .y1((d) => yScalePositive(d[1])));
        setPositiveDrawn(true);
    }, [
        svgPath,
        serie,
        positiveExtents,
        extents,
        stackedPositive,
        margin,
        positiveDrawn,
    ]);
    // draw negative chart
    useEffect(() => {
        if (!svgPath ||
            serie.data.length === 0 ||
            stackedNegative.length === 0 ||
            negativeDrawn)
            return;
        const { minX, minY, maxX, maxY } = negativeExtents;
        if ([minX, minY, maxX, maxY, extents.minY, extents.maxY].some((el) => el === undefined))
            return;
        const { keysNegative, colorsNegative } = serie;
        const colorScaleNegative = scaleOrdinal()
            .domain(keysNegative)
            .range(colorsNegative);
        const xScale = scaleLinear()
            .domain([minX, maxX])
            .range([margin.left, width - margin.right]);
        const xAxisShift = getXAxisShift({
            height,
            margin,
            extents,
            positiveExtents,
        });
        if (xAxisShift === undefined)
            return;
        const yScaleNegative = scaleLinear()
            .domain([minY, maxY])
            .range([height - margin.bottom, xAxisShift]);
        //@ts-ignore
        svgPath
            .remove()
            .data(stackedNegative)
            .enter()
            .append("path")
            .style("fill", (d) => colorScaleNegative(colorsNegative[d.index]))
            .attr("d", 
        //@ts-ignore
        area()
            //@ts-ignore
            .x((d) => xScale(d.data.x))
            .y0((d) => yScaleNegative(d[0]))
            .y1((d) => yScaleNegative(d[1])));
        setNegativeDrawn(true);
    }, [
        svgPath,
        serie,
        negativeExtents,
        extents,
        stackedNegative,
        margin,
        negativeDrawn,
    ]);
    // draw totals line
    useEffect(() => {
        if (!svg || totals.length === 0 || !positiveDrawn || !negativeDrawn)
            return;
        const { minX, maxX, minY, maxY } = extents;
        if (isVoid(minX) || isVoid(maxX) || isVoid(minY) || isVoid(maxY))
            return;
        const xScale = scaleLinear()
            .domain([minX, maxX])
            .range([margin.left, width - margin.right]);
        const yScale = scaleLinear()
            .domain([minY, maxY])
            .range([height - margin.bottom, margin.top]);
        svg
            .append("path")
            .datum(totals)
            .attr("fill", "none")
            .attr("stroke", "#E8662D")
            .attr("stroke-width", "3px")
            .attr("d", line()
            .x((d) => xScale(d[0]))
            .y((d) => yScale(d[1])));
    }, [
        svg,
        totals,
        extents,
        positiveDrawn,
        negativeDrawn,
        margin,
        width,
        height,
    ]);
    // draw x axis
    useEffect(() => {
        const { minX, maxX } = extents;
        if (!svg || minX === undefined || maxX === undefined)
            return;
        const xAxisShift = getXAxisShift({
            height,
            margin,
            extents,
            positiveExtents,
        });
        if (xAxisShift === undefined)
            return;
        const xScale = scaleLinear()
            .domain([minX, maxX])
            .range([margin.left, width - margin.right]);
        const xAxis = axisBottom(xScale)
            .ticks(0)
            .tickFormat(formatterX)
            .tickSizeOuter(0);
        svg
            .append("g")
            .classed(`xAxis`, true)
            .call(xAxis)
            .attr("transform", "translate(0, " + xAxisShift + ")")
            .attr("stroke-width", "1px");
    }, [svg, extents, positiveExtents, ticksX, formatterX]);
    // draw left y axis
    useEffect(() => {
        const { minY, maxY } = extents;
        if (!svg || minY === undefined || maxY === undefined)
            return;
        const yScale = scaleLinear()
            .domain([minY, maxY])
            .range([height - margin.bottom, margin.top]);
        const yAxisLeft = axisLeft(yScale)
            .ticks(0)
            .tickSizeOuter(0)
            .tickFormat(formatterYLeft);
        svg
            .append("g")
            .classed(`yAxis`, true)
            .call(yAxisLeft)
            .attr("transform", "translate(" + margin.left + ", 0)")
            .attr("stroke-width", "1px");
    }, [svg, extents, formatterYLeft]);
    // draw and subscirbe focus to mouse events
    useEffect(() => {
        if (hasListeners)
            return;
        if (!svg)
            return;
        const { minX, maxX, minY, maxY } = extents;
        if (isVoid(minX) || isVoid(maxX) || isVoid(minY) || isVoid(maxY))
            return;
        const svgNode = svg.node();
        const tooltip = select("." + getTooltipClass(title));
        if (!svgNode)
            return;
        const svgBox = svgNode.getBoundingClientRect();
        const focus = drawFocus({ svg, color: "#E8662D" });
        const xScale = scaleLinear()
            .domain([minX, maxX])
            .range([margin.left, width - margin.right]);
        const yScale = scaleLinear()
            .domain([minY, maxY])
            .range([height - margin.bottom, margin.top]);
        svg.on("mousemove", (event) => {
            const { x } = event;
            const xAdjusted = x - svgBox.left;
            const closest = getClosest({ x: xAdjusted, xScale, serie: totals });
            showFocus({ focus, xScale, yScale, closest });
            showTooltip({ x, tooltip, closest, xScale, yScale, width, serie });
        });
        svg.on("mouseout", () => {
            hideFocus(focus);
            hideTooltip(tooltip);
        });
        setHasListeners(true);
    }, [svg, extents, totals, hasListeners, title, width, serie]);
};
//# sourceMappingURL=hooks.js.map