import {
  fillTermWrapper,
  termsettingInit
} from "./chunk-SA7APTJR.js";
import {
  isNumericTerm
} from "./chunk-NOEAT6CX.js";
import {
  select_default
} from "./chunk-I6Y4O3RR.js";

// plots/matrix/matrix.renderers.js
function setRenderers(self) {
  self.render = function() {
    const s = self.settings.matrix;
    const l = self.layout;
    const d = self.dimensions;
    const duration = self.dom.svg.attr("width") ? s.duration : 0;
    self.dom.clipRect.attr("x", d.xOffset - 1).attr("y", 0).attr("width", d.mainw + 3).attr("height", d.mainh + 500);
    self.renderSerieses(s, l, d, duration);
    self.renderLabels(s, l, d, duration);
    self.renderDivideByLabel(s, l, d, duration);
    self.dom.colBeam.attr("width", d.dx).attr("height", d.mainh).style("stroke", s.beamStroke);
    self.dom.rowBeam.attr("width", d.zoomedMainW).attr("height", s.rowh).style("stroke", s.beamStroke);
  };
  self.renderSerieses = function(s, l, d, duration) {
    if (self.prevUseCanvas != s.useCanvas) {
      self.dom.seriesesG.selectAll("g").remove();
    }
    if (s.useCanvas) {
      const _g = self.dom.seriesesG.selectAll("g");
      const g = (
        /*(_g.size() && _g) ||*/
        self.dom.seriesesG.append("g").datum(this.serieses)
      );
      self.renderCanvas(this.serieses, g, d, s, _g, duration);
    } else {
      self.dom.seriesesG.attr("transform", `translate(${d.xOffset + d.seriesXoffset},${d.yOffset})`);
      const sg = self.dom.seriesesG.selectAll(".sjpp-mass-series-g").data(this.serieses, (series) => series.tw.$id);
      sg.exit().remove();
      sg.each(self.renderSeries);
      sg.enter().append("g").attr("class", "sjpp-mass-series-g").style("opacity", 1e-3).each(self.renderSeries);
      self.mouseout();
    }
    self.prevUseCanvas = s.useCanvas;
  };
  self.renderSeries = async function(series) {
    const s = self.settings.matrix;
    const d = self.dimensions;
    const g = select_default(this);
    const duration = g.attr("transform") ? s.duration : 0;
    g.attr("transform", `translate(${series.x},${series.y})`).style("opacity", 1);
    const last = series.cells[series.cells.length - 1];
    const height = series.y + last?.y + s.rowh;
    const rects = g.selectAll("rect").data(series.cells, (cell) => cell.sample + ";;" + cell.tw.$id + ";;" + cell.valueIndex);
    rects.exit().remove();
    rects.each(self.renderCell);
    rects.enter().append("rect").each(self.renderCell);
  };
  self.renderCanvas = async function(serieses, g, d, s, _g, duration) {
    const pxr = window.devicePixelRatio <= 1 ? 1 : window.devicePixelRatio;
    g.selectAll("*").remove();
    const width = d.imgW;
    const height = self.dimensions.mainh;
    const canvas = window.OffscreenCanvas ? new OffscreenCanvas(width * pxr, height * pxr) : (
      // TODO: no need to support older browser versions???
      self.dom.holder.append("canvas").attr("width", pxr * width).attr("height", pxr * height).style("opacity", 0).node()
    );
    const ctx = canvas.getContext("2d");
    ctx.imageSmoothingEnabled = false;
    ctx.imageSmoothingQuality = "high";
    ctx.scale(pxr, pxr);
    for (const series of serieses) {
      for (const cell of series.cells) {
        self.renderCellWithCanvas(ctx, cell, series, s, d, series.y);
      }
    }
    if (window.OffscreenCanvas) {
      const reader = new FileReader();
      reader.addEventListener(
        "load",
        () => {
          _g?.remove();
          self.dom.seriesesG.attr("transform", `translate(${d.xOffset + d.seriesXoffset},${d.yOffset})`);
          g.selectAll("image").remove();
          g.append("image").attr("xlink:href", reader.result).attr("x", d.xMin).attr("width", width).attr("height", height);
        },
        false
      );
      const blob = await canvas.convertToBlob({ quality: 1 });
      const dataURL = reader.readAsDataURL(blob);
    } else {
      _g?.remove();
      self.dom.seriesesG.attr("transform", `translate(${d.xOffset + d.seriesXoffset},${d.yOffset})`);
      const dataURL = canvas.toDataURL();
      const ratio = window.devicePixelRatio * window.devicePixelRatio;
      g.append("image").attr("width", width).attr("height", height).attr("xlink:href", dataURL);
      if (!window.OffscreenCanvas) canvas.remove();
    }
    self.mouseout();
  };
  self.renderCellWithCanvas = function(ctx, cell, series, s, d, _y) {
    if (!cell.fill)
      cell.fill = cell.$id in self.colorScaleByTermId ? self.colorScaleByTermId[cell.$id](cell.key) : getRectFill(cell);
    const x = cell.x ? cell.x - d.xMin : 0;
    const y = _y ? _y + cell.y : cell.y || 0;
    const width = s.useMinPixelWidth ? Math.max(cell.width || d.colw, d.pxw) : cell.width || d.colw;
    const height = "height" in cell ? cell.height : s.rowh;
    ctx.fillStyle = cell.fill;
    ctx.fillRect(x, y, width, height);
    const borderWidth = Math.min(width, height) * 0.1;
    if (cell.border) {
      ctx.lineWidth = borderWidth;
      ctx.strokeStyle = "white";
      ctx.strokeRect(x, y, width, height);
    }
  };
  self.renderCell = function(cell) {
    if (!cell.fill)
      cell.fill = cell.$id in self.colorScaleByTermId ? self.colorScaleByTermId[cell.$id](cell.key) : getRectFill(cell);
    const s = self.settings.matrix;
    const rect = select_default(this).attr("x", cell.x || 0).attr("y", cell.y || 0).attr("width", cell.width || self.dimensions.colw).attr("height", "height" in cell ? Math.max(0, cell.height) : s.rowh).attr("shape-rendering", "crispEdges").attr("fill", cell.fill);
    if (cell.border) {
      rect.attr("stroke", "white").attr("stroke-width", 0.8);
    }
  };
  self.renderLabels = function(s, l, d, duration) {
    for (const direction of ["top", "btm", "left", "right"]) {
      let renderLabel2 = function(lab) {
        const g = select_default(this);
        g.attr("transform", side.attr.labelGTransform);
        if (!g.select(":scope>text").size()) g.append("text");
        const showContAxis = !side.isGroup && lab.grp?.type !== "hierCluster" && lab.tw?.q?.mode == "continuous";
        const labelText = side.label(lab);
        const text = g.select(":scope>text").attr("fill", "#000");
        let continuousBarHAdjust;
        const twSpecificSettings = self.config.settings.matrix.twSpecificSettings;
        const twSettingsBarH = twSpecificSettings[lab.tw?.$id]?.contBarH;
        if (twSettingsBarH && s.barh) continuousBarHAdjust = (twSettingsBarH - s.barh) * 0.5;
        text.attr(
          "display",
          lab.grp?.type === "hierCluster" && s.clusterRowh < 6 ? "none" : side.attr.fontSize < 6 || labelText === "configure" ? "none" : ""
        ).attr("font-size", lab.grp?.type === "hierCluster" ? Math.max(4, s.clusterRowh - 4) : side.attr.fontSize).attr("text-anchor", side.attr.labelAnchor).attr(
          "transform",
          side.attr.labelTransform + (continuousBarHAdjust ? ` translate(0,${continuousBarHAdjust})` : "")
        ).attr("cursor", "pointer").attr(side.attr.textpos.coord, side.attr.textpos.factor * (showContAxis ? 30 : 0));
        if (!Array.isArray(labelText)) {
          text.text(labelText);
          text.attr(
            "y",
            lab.grp?.type !== "hierCluster" && lab.tw?.q?.mode == "continuous" ? 10 : lab.grp?.type === "hierCluster" ? 0.1 * s.clusterRowh : 0
          );
          if (lab.grp?.type !== "hierCluster" && lab.tw?.q?.mode == "continuous") text.attr("x", -20);
        } else {
          text.text("");
          const tspan = text.selectAll("tspan").data(labelText);
          tspan.enter().append("tspan").attr("class", getTspanCls2).attr("dx", getTspanDx2).attr("font-size", getTspanFontSize2).text(getTspanText2);
        }
        text.on("mouseover", labelText === "configure" ? () => text.attr("opacity", 0.5) : null).on("mouseout", labelText === "configure" ? () => text.attr("opacity", 0) : null);
        const hasAxis = g.select(".sjpp-matrix-cell-axis").size() && true;
        if (showContAxis && labelText) {
          if (!hasAxis) {
            g.append("g").attr("class", "sjpp-matrix-cell-axis").attr("shape-rendering", "crispEdges");
          }
          const axisg = g.select(".sjpp-matrix-cell-axis");
          axisg.selectAll("*").remove();
          const domain = [lab.counts.maxval, lab.counts.minval];
          if (s.transpose) domain.reverse();
          const twSpecificSettings2 = self.config.settings.matrix.twSpecificSettings;
          const twSettings = twSpecificSettings2[lab.tw.$id];
          const x = !s.transpose ? 0 : twSettings.contBarGap - 1 - lab.labelOffset;
          const y = !s.transpose ? twSettings.contBarGap - 1 - lab.labelOffset : 0;
          axisg.attr("shape-rendering", "crispEdges").attr("transform", `translate(${x},${y})`).call(side.attr.axisFxn(lab.scales.full.domain(lab.scales.tickValues)).tickValues(lab.scales.tickValues));
        } else if (hasAxis) {
          g.select(".sjpp-matrix-cell-axis").remove();
        }
      }, getTspanCls2 = function(d2) {
        return d2.cls;
      }, getTspanDx2 = function(d2) {
        return d2.dx;
      }, getTspanFontSize2 = function(d2) {
        return d2.fontSize || side.attr.fontSize;
      }, getTspanText2 = function(d2) {
        return d2.text;
      };
      var renderLabel = renderLabel2, getTspanCls = getTspanCls2, getTspanDx = getTspanDx2, getTspanFontSize = getTspanFontSize2, getTspanText = getTspanText2;
      const side = l[direction];
      side.box.style("display", side.display || "").attr("transform", side.attr.boxTransform);
      const labels = side.box.selectAll(".sjpp-matrix-label").data(side.data, side.key);
      labels.exit().remove();
      labels.each(renderLabel2);
      labels.enter().append("g").attr("class", "sjpp-matrix-label").each(renderLabel2);
    }
  };
  self.colLabelGTransform = (lab, grpIndex) => {
    const s = self.settings.matrix;
    const d = self.dimensions;
    lab.labelOffset = 0.8 * d.colw;
    const x = lab.grpIndex * s.colgspace + lab.totalIndex * d.dx + lab.labelOffset + lab.totalHtAdjustments;
    const y = 0;
    return `translate(${x + d.seriesXoffset},${y})`;
  };
  self.colGrpLabelGTransform = (lab, grpIndex) => {
    const s = self.settings.matrix;
    const d = self.dimensions;
    const len = (lab.processedLst || lab.grp.lst).length;
    const x = lab.grpIndex * s.colgspace + lab.prevGrpTotalIndex * d.dx + len * d.dx / 2 + s.grpLabelFontSize / 2 + lab.totalHtAdjustments;
    return `translate(${x + d.seriesXoffset},0)`;
  };
  self.rowLabelGTransform = (lab, grpIndex) => {
    const s = self.settings.matrix;
    const d = self.dimensions;
    const x = 0;
    lab.labelOffset = 0.7 * (lab.grp.type == "hierCluster" ? s.clusterRowh : s.rowh);
    const y = lab.grpIndex * s.rowgspace + lab.totalIndex * d.dy + lab.labelOffset + lab.totalHtAdjustments;
    return `translate(${x},${y})`;
  };
  self.rowGrpLabelGTransform = (lab, grpIndex) => {
    const s = self.settings.matrix;
    const d = self.dimensions;
    const len = (lab.processedLst || lab.grp.lst).length;
    const x = lab.tw?.q?.mode == "continuous" ? 20 : 0;
    const y = lab.grpIndex * s.rowgspace + lab.prevGrpTotalIndex * d.dy + len * d.dy / 2 + s.grpLabelFontSize / 2 + lab.totalHtAdjustments;
    return `translate(${x},${y})`;
  };
  self.rowAxisGTransform = (lab, grpIndex) => {
    const s = self.settings.matrix;
    const d = self.dimensions;
    const x = 0;
    const y = lab.grpIndex * s.rowgspace + lab.totalIndex * d.dy + 0.7 * s.rowh + lab.totalHtAdjustments;
    return `translate(${x},${y})`;
  };
  self.renderDivideByLabel = async (s, l, d) => {
    self.dom.mainG.selectAll(".sjpp-matrix-divide-by-label").remove();
    if (!self.config.divideBy) return;
    const name = self.config.divideBy?.term.name || "";
    const text = name.length <= s.rowlabelmaxchars ? name : name.slice(0, s.rowlabelmaxchars) + "\u2026";
    const sides = !s.transpose ? [l.left, l.right] : [l.top, l.bottom];
    const box = sides.find((d2) => !d2.isGroup)?.box;
    const y = (s.collabelpos == "top" ? d.mainh + s.collabelmaxchars : -s.collabelmaxchars) + 8;
    const anchor = s.rowlabelpos == "left" ? "end" : "start";
    const cl = s.controlLabels;
    const gNote = box.append("g").attr("class", "sjpp-matrix-divide-by-label").attr("transform", `translate(0, ${y})`);
    gNote.append("text").attr("text-anchor", anchor).attr("font-style", "italic").attr("y", -20).text(`${cl.Samples} grouped by`);
    const g = box.datum({ tw: self.config.divideBy }).append("g").attr("class", "sjpp-matrix-divide-by-label").attr("transform", `translate(0, ${y})`).on("click", (event, d2) => {
      pill.showMenu(event, textElem.node());
    });
    const textElem = g.append("text").attr("text-anchor", anchor).attr("font-weight", 600).text(text);
    g.append("title").text(`${cl.Samples} are grouped by this gene or variable. Click to edit.`);
    const customMenuOptions = [];
    const tvsKey = isNumericTerm(self.config.divideBy.term) ? "ranges" : "values";
    if (self.config.legendValueFilter.lst?.find(
      (l2) => l2.legendGrpName == self.config.divideBy.term.id || l2.legendGrpName == self.config.divideBy.term.name
    )?.tvs[tvsKey]?.length) {
      customMenuOptions.push({ label: `Show filtered ${cl.samples}`, callback: self.showDeletedSampleGroups });
    }
    const pill = await termsettingInit({
      menuOptions: "{edit,replace,remove}",
      //numericEditMenuVersion: opts.numericEditMenuVersion,
      customMenuOptions,
      //custom menu options other than menuOptions
      vocabApi: self.app.vocabApi,
      vocab: self.state.vocab,
      //activeCohort: opts.state?.activeCohort,
      holder: g,
      debug: self.opts.debug,
      usecase: { target: "matrix" },
      getBodyParams: () => {
        const currentGeneNames = self.termOrder.filter((t) => t.tw.term.type === "geneVariant").map(
          (t) => t.tw.term.chr ? `${t.tw.term.chr}:${t.tw.term.start}-${t.tw.term.stop}` : t.tw.term.gene || t.tw.term.name
        );
        if (currentGeneNames.length) return { currentGeneNames };
        return {};
      },
      callback: async (tw) => {
        if (self.dom.loadingDiv && self.dom.svg) {
          self.dom.loadingDiv.selectAll("*").remove();
          self.dom.loadingDiv.html("").style("display", "").style("position", "relative").style("left", "45%");
          self.dom.loadingDiv.html("Processing data ...");
          self.dom.svg.style("opacity", 0.1).style("pointer-events", "none");
        }
        if (tw && !tw.q) throw "data.q{} missing from pill callback";
        if (tw?.term && isNumericTerm(tw.term)) {
          tw.q = { ...tw.q, mode: "discrete" };
        }
        if (tw) await fillTermWrapper(tw, self.app.vocabApi);
        await pill.main(tw ? tw : { term: null, q: null });
        box.datum({ tw });
        self.app.dispatch({
          type: "plot_edit",
          id: self.id,
          config: {
            divideBy: tw,
            legendValueFilter: self.mayRemoveTvsEntry(self.config.divideBy)
          }
        });
      }
    });
    const arg = {
      term: self.config.divideBy.term,
      q: self.config.divideBy.q
    };
    if (self.config.divideBy.$id) arg.$id = self.config.divideBy.$id;
    pill.main(arg);
  };
  self.adjustSvgDimensions = async function(prevTranspose) {
    const s = self.settings.matrix;
    const hc = self.settings.hierCluster || {};
    const l = self.layout;
    const hcHeight = !hc.yDendrogramHeight ? 0 : hc.yDendrogramHeight + (l.top.display === "none" ? 0 : 10);
    const hcWidth = hc.xDendrogramHeight || 0;
    const d = self.dimensions;
    const duration = self.dom.svg.attr("width") ? s.duration : 0;
    await sleep(prevTranspose == s.transpose ? duration : s.duration);
    const topBox = l.top.box.node().getBBox();
    const btmBox = l.btm.box.node().getBBox();
    const leftBox = l.left.box.node().getBBox();
    const rtBox = l.right.box.node().getBBox();
    const legendBox = self.dom.legendG.node().getBBox();
    const seriesBox = self.dom.seriesesG.node().getBBox();
    d.extraWidth = leftBox.width + rtBox.width + s.margin.left + s.margin.right + s.rowlabelgap * 2;
    d.extraHeight = topBox.height + btmBox.height + s.margin.top + s.margin.bottom + s.collabelgap * 2;
    d.svgw = d.mainw + d.extraWidth + hcWidth;
    d.svgh = d.mainh + d.extraHeight + legendBox.height + 20 + s.scrollHeight + hcHeight;
    self.dom.svg.attr("width", d.svgw).attr("height", d.svgh);
    let maxLabelWidth = self.type == "hierCluster" ? 0 : leftBox.width, maxLabelNumChars = 0;
    if (hc.xDendrogramHeight) {
      self.dom.termLabelG.selectAll(".sjpp-matrix-label").each(function(d2) {
        if (d2.grp.type !== "hierCluster") return;
        const box = this.getBBox();
        if (box.width > maxLabelWidth) {
          maxLabelWidth = box.width;
          maxLabelNumChars = d2.label.length;
        }
      });
    }
    const x = -l.left.offset + hcWidth + maxLabelWidth;
    const xAdjust = !hc.xDendrogramHeight ? 0 : Math.max(leftBox.width - (hc.xDendrogramHeight + maxLabelWidth), 0);
    const y = (l.top.display == "none" ? 0 : topBox.height) - l.top.offset + hcHeight;
    self.dom.mainG.attr("transform", `translate(${x + xAdjust},${y})`);
    self.dom.clipRect.attr("y", -y).attr("height", d.mainh + 500 + y);
    const legendX = d.xOffset + (s.transpose ? 20 : 0);
    const legendY = d.yOffset + d.mainh + s.collabelgap + (l.btm.display == "none" ? 0 : btmBox.height) + 20;
    self.dom.legendG.attr("transform", `translate(${legendX},${legendY})`);
    if (hc.xDendrogramHeight) {
      const dendroX = maxLabelWidth + xAdjust - l.left.offset + d.xOffset - d.dx / 2;
      self.dom.hcClipRect.attr("x", dendroX + hcWidth + d.dx / 2).attr("y", 0).attr("width", d.mainw + 3).attr("height", d.mainh + hc.yDendrogramHeight + 500);
      self.topDendroX = dendroX + d.seriesXoffset;
      self.dom.topDendrogram.attr("transform", `translate(${self.topDendroX}, 0)`);
      const y2 = l.top.display == "none" ? 0 : topBox.height + s.collabelgap;
      self.dom.leftDendrogram.attr("transform", `translate(${dendroX - maxLabelWidth - 10}, ${y2})`);
    }
  };
}
function getRectFill(d) {
  if (d.fill) return d.fill;
  const cls = d.class || Array.isArray(d.values) && d.values[0].class;
  if (!cls) console.log;
  return cls ? mclass[cls].color : "#555";
}
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export {
  setRenderers
};
//# sourceMappingURL=chunk-3ZLJDFJU.js.map
