import {
  Menu
} from "./chunk-HYOEWQ5P.js";
import {
  pointer_default,
  select_default
} from "./chunk-I6Y4O3RR.js";

// dom/svgSeriesTips.js
function getSeriesTip(line, rect, _tip = null) {
  const tip = _tip || new Menu({ padding: "5px" });
  line.style("display", "none");
  const rectNode = rect.style("fill", "transparent").node();
  function mouseOver(event) {
    const m = pointer_default(event, rectNode);
    const mx = m[0];
    const xVal = +opts.xScale.invert(mx).toFixed(opts.decimals);
    const x = opts.xScale(xVal);
    line.style("display", "").attr("stroke", "#aaa").attr("stroke-dasharray", 4).attr("x1", x).attr("x2", x);
    const seriesHtmls = [];
    for (const series of opts.serieses) {
      const data = series.data;
      const data_x = data.map((d) => d.x);
      if (xVal >= Math.min(...data_x) && xVal <= Math.max(...data_x)) {
        const max = Math.max(...data_x.filter((x2) => x2 <= xVal));
        const timepoint = data.find((d) => d.x == max);
        if (timepoint) seriesHtmls.push(timepoint.html);
      }
    }
    if (seriesHtmls.length) {
      tip.show(event.clientX, event.clientY).d.html(`<span>${opts.xTitleLabel}: ${xVal}</span><br>` + seriesHtmls.map((d) => d).join(opts.separator));
    } else {
      tip.hide();
    }
  }
  rect.on("mouseover", mouseOver).on("mousemove", mouseOver).on("mouseout", () => {
    line.style("display", "none");
    tip.hide();
  });
  const opts = {
    separator: "<br>",
    decimals: 1
  };
  return {
    /*
    			Will update the length of the vertical line
    			and reassign optional data values
    
    			!!! MUST call api.update() before the expected mouseover event,
    			so that the line will have the proper length and the mouse
    			position could be computed with .xScale !!! 
    
    			_opts{}
    			.xScale					required
    										the d3-scale object that was used for the rect dimensions
    										
    			.xTitleLabel			required
    										title of x-axis
    										will be used as label of x-value in tooltip
    
    			.serieses[{data}] 		required
    										array of series objects, assumed to be visibly rendered
    							
    				.data[{x, html}]	required
    										array of data objects
    										the series data that is currently rendered in the chart
    					
    					.x 				required, float
    										the datapoint's actual, unscaled x value
    					
    					.html   		required, string
    										the HTML to display in the tooltip if this datapoint's 
    										x value matched the vertical line's position
    
    			.separator				optional, string
    										the html to be used to join the series html strings when displayed in the tooltip
    
    			.decimals				optional, number
    										number of decimal places of the datapoint's x-value
    										will control the precision of the vertical line
    		*/
    update(_opts = {}) {
      Object.assign(opts, _opts);
      const x = rect.attr("x");
      const y = rect.attr("y");
      line.attr("x1", x).attr("x2", x).attr("y1", y).attr("y2", rect.attr("height") - y);
    },
    /*
    	detroy to help minimize memory leaks
    	from elements and event handlers not being garbage collected
    	because of non-deactivated references
    */
    destroy() {
      rect.on("mouseover", null).on("mousemove", null).on("mouseout", null);
    }
  };
}

// dom/renderAtRisk.js
function renderAtRiskG({ g, s, chart, order, term2toColor, onSerieClick }) {
  const atRiskGroups = [];
  const xTickValues = chart.xTickValues.filter((xTick) => xTick >= chart.xMin);
  for (const series of chart.visibleSerieses) {
    const counts = [];
    let i = 0, d = series.data[0], prev = d, nCensored = 0;
    for (const time of xTickValues) {
      while (d && d.x < time) {
        nCensored += d.ncensor;
        prev = d;
        i++;
        d = series.data[i];
      }
      if (d && d.x === time) {
        counts.push([time, d.nrisk, nCensored]);
      } else {
        counts.push([time, prev.nrisk - prev.nevent - prev.ncensor, nCensored]);
      }
    }
    const { seriesId, seriesLabel } = series;
    atRiskGroups.push({ seriesId, seriesLabel, counts });
  }
  const y = s.svgh - s.svgPadding.top - s.svgPadding.bottom + 60;
  const seriesOrder = order || chart.serieses.map((s2) => s2.seriesId);
  g.selectAll(".sjpp-atrisk-title").remove();
  if (s.atRiskVisible) {
    atRiskGroups.sort((a, b) => seriesOrder.indexOf(a.seriesId) - seriesOrder.indexOf(b.seriesId));
    const addYoffset = chart.serieses.length == 1 && !chart.serieses[0].seriesId;
    const titleg = g.append("text").attr("class", "sjpp-atrisk-title").attr("transform", `translate(${s.atRiskLabelOffset}, ${addYoffset ? 2 * s.axisTitleFontSize : 0})`).attr("text-anchor", "end").attr("font-size", `${s.axisTitleFontSize - 4}px`).attr("cursor", chart.serieses.length == 1 ? "pointer" : "default").text("Number at risk").on("click", chart.serieses.length == 1 ? (e) => onSerieClick({ seriesId: "" }, e.clientX, e.clientY) : null);
    if (term2toColor[""]) titleg.style("fill", s.defaultColor);
    titleg.append("tspan").attr("x", 0).attr("y", s.axisTitleFontSize - 4).text("(# censored)");
  } else {
    atRiskGroups.length = 0;
  }
  const sg = g.attr("transform", `translate(0,${y})`).selectAll(":scope > g").data(atRiskGroups, (s2) => s2.seriesLabel || s2.seriesId);
  sg.exit().remove();
  sg.each(function(atRiskGroup, i) {
    const { seriesId, seriesLabel, counts } = atRiskGroup;
    const y2 = (i + 1) * (2 * s.axisTitleFontSize);
    const g2 = select_default(this).attr("transform", `translate(0,${y2})`).attr("fill", term2toColor[""] ? s.defaultColor : term2toColor[seriesId].adjusted);
    let legendText = g2.select(":scope>text");
    if (!legendText.size()) legendText = g2.append("text");
    legendText.text(seriesId && seriesId !== "*" ? seriesLabel || seriesId : "");
    let ticksG = g2.select(":scope>g");
    if (!ticksG.size()) ticksG = g2.append("g");
    renderAtRiskTick(ticksG, chart, xTickValues, s, atRiskGroup);
  });
  sg.enter().append("g").each(function(atRiskGroup, i) {
    const { seriesId, seriesLabel, counts } = atRiskGroup;
    const y2 = (i + 1) * (2 * s.axisTitleFontSize);
    const g2 = select_default(this).attr("transform", `translate(0,${y2})`).attr("fill", term2toColor[""] ? s.defaultColor : term2toColor[seriesId].adjusted).on("click", (e) => onSerieClick({ seriesId }, e.clientX, e.clientY));
    g2.append("text").attr("data-testid", "sjpp-atrisk-seriesId").attr("transform", `translate(${s.atRiskLabelOffset}, 0)`).attr("text-anchor", "end").attr("font-size", `${s.axisTitleFontSize - 4}px`).attr("cursor", "pointer").datum({ seriesId }).text(seriesId && seriesId !== "*" ? seriesLabel || seriesId : "");
    renderAtRiskTick(g2.append("g"), chart, xTickValues, s, atRiskGroup);
  });
}
function renderAtRiskTick(g, chart, xTickValues, s, atRiskGroup) {
  const { seriesId, counts } = atRiskGroup;
  const reversed = counts.slice().reverse();
  const data = xTickValues.map((tickVal) => {
    if (tickVal === 0) return { seriesId, tickVal, atRisk: counts[0][1], nCensored: counts[0][2] };
    const d = reversed.find((d2) => d2[0] <= tickVal);
    return { seriesId, tickVal, atRisk: d[1], nCensored: d[2] };
  });
  const text = g.selectAll("text").data(data);
  text.exit().remove();
  text.attr("transform", (d) => `translate(${chart.xScale(d.tickVal)},0)`).attr("text-anchor", "middle").attr("font-size", `${s.axisTitleFontSize - 4}px`).attr("cursor", "pointer").each(renderAtRiskLabel);
  text.enter().append("text").attr("transform", (d) => `translate(${chart.xScale(d.tickVal)},0)`).attr("text-anchor", "middle").attr("font-size", `${s.axisTitleFontSize - 4}px`).attr("cursor", "pointer").each(renderAtRiskLabel);
  function renderAtRiskLabel(d) {
    const tspans = select_default(this).selectAll("tspan").data([d.atRisk, `(${d.nCensored})`]);
    tspans.exit().remove();
    tspans.attr("y", (d2, i) => i === 0 ? 0 : i * (s.axisTitleFontSize - 4)).text((d2) => d2);
    tspans.enter().append("tspan").attr("x", 0).attr("y", (d2, i) => i === 0 ? 0 : i * (s.axisTitleFontSize - 4)).text((d2) => d2);
  }
}

// dom/renderPvalueTable.js
function renderPvalues({
  title,
  titleTestid,
  holder,
  plot,
  tests,
  s,
  bins,
  tip,
  setActiveMenu,
  updateHiddenPvalues
}) {
  let fontSize;
  if (s.axisTitleFontSize) {
    fontSize = s.axisTitleFontSize - 2;
  } else fontSize = 15;
  const maxPvalsToShow = 10;
  holder.selectAll("*").remove();
  if (!plot) {
    throw `plot type '${plot}' not recognized`;
  }
  holder.append("div").style("padding-bottom", "5px").style("font-size", fontSize + "px").style("font-weight", "bold").attr("data-testid", titleTestid || "sjpp-pvalueTable-title").text(title);
  const tablediv = holder.append("div").style("border", "1px solid #ccc");
  if (plot == "violin" && tests.pvalues.length > maxPvalsToShow || tests.length > maxPvalsToShow) {
    tablediv.style("overflow", "auto").style("height", "220px");
  }
  const visibleTests = s.hiddenPvalues ? tests.filter((t) => !s.hiddenPvalues.find((p) => p.series1.id === t.series1.id && p.series2.id === t.series2.id)) : tests;
  if (plot == "violin" ? visibleTests.pvalues.length : visibleTests.length) {
    const binOrder = bins && bins.length > 0 ? bins.map((b) => b.label) : null;
    if (binOrder) {
      for (const test of visibleTests) {
        const orderedSeries = [test.series1.id, test.series2.id].sort(
          (a, b) => binOrder.indexOf(a) - binOrder.indexOf(b)
        );
        if (test.series2.id == orderedSeries[0]) {
          test.series1_new = test.series2;
          test.series2_new = test.series1;
          test.series1 = test.series1_new;
          test.series2 = test.series2_new;
          delete test.series1_new;
          delete test.series2_new;
        }
      }
      visibleTests.sort(
        (a, b) => binOrder.indexOf(a.series1.id) - binOrder.indexOf(b.series1.id) || binOrder.indexOf(a.series2.id) - binOrder.indexOf(b.series2.id)
      );
    }
    const table = tablediv.append("table").style("width", "100%");
    table.append("thead").append("tr").selectAll("td").data(["Group 1", "Group 2", "P-value"]).enter().append("td").style("padding", "1px 8px 1px 2px").style("color", "#555").style("position", "sticky").style("top", "0px").style("background", "white").style("font-size", fontSize + "px").text((column) => column);
    const tbody = table.append("tbody");
    const tr = tbody.selectAll("tr").data(plot == "violin" ? visibleTests.pvalues : visibleTests).enter().append("tr").attr("class", `pp-${plot}-chartLegends-pvalue`);
    if (plot == "survival") {
      tr.on("click", (event, t) => {
        const hiddenPvalues = s.hiddenPvalues.slice();
        hiddenPvalues.push(t);
        updateHiddenPvalues(hiddenPvalues);
      });
    }
    tr.selectAll("td").data((d) => [d.series1, d.series2, d.pvalue]).enter().append("td").attr("aria-label", plot ? "Click to hide a p-value" : "").style("color", plot == "violin" ? "black" : (d) => d.color).style("padding", "1px 8px 1px 2px").style("font-size", fontSize + "px").style("cursor", plot == "survival" ? "pointer" : "auto").text((d) => plot == "violin" ? d : d.text);
    if (plot == "cuminc") {
      if (visibleTests.find((test) => test.permutation)) {
        holder.append("div").style("margin-top", "10px").style("font-size", fontSize - 2 + "px").text("*computed by permutation of Gray's test statistic");
      }
    }
  }
  if (plot == "survival") {
    const hiddenTests = tests.filter(
      (t) => s.hiddenPvalues.find((p) => p.series1.id === t.series1.id && p.series2.id === t.series2.id)
    );
    if (hiddenTests.length) {
      holder.append("div").style("color", "#aaa").style("cursor", "pointer").html(`<span style='color:#aaa; font-weight:400'><span>Hidden tests (${hiddenTests.length})</span>`).on("click", (event) => {
        tip.clear();
        const divs = tip.d.append("div").selectAll("div").data(hiddenTests).enter().append("div").each(function(d) {
          setActiveMenu(true);
          const div = select_default(this);
          div.append("input").attr("type", "checkbox").style("margin-right", "5px");
          div.append("span").html(`${d.series1.id} vs ${d.series2.id}`);
        });
        tip.d.append("button").html("Show checked test(s)").on("click", () => {
          const hiddenPvalues = [];
          divs.filter(function() {
            return !select_default(this.firstChild).property("checked");
          }).each((d) => hiddenPvalues.push(d));
          updateHiddenPvalues(hiddenPvalues);
          tip.hide();
        });
        tip.show(event.clientX, event.clientY);
      });
    }
    if (s.maxTimeToEvent && visibleTests.length) {
      holder.append("div").style("margin-top", "10px").style("font-size", fontSize - 2 + "px").text((visibleTests.length > 1 ? "p-values are" : "p-value is") + " computed with all survival data");
    }
  }
}

export {
  getSeriesTip,
  renderAtRiskG,
  renderPvalues
};
//# sourceMappingURL=chunk-KI5KI3ZJ.js.map
