import {
  NUMERIC_DICTIONARY_TERM,
  TermTypes
} from "./chunk-NOEAT6CX.js";
import {
  __export
} from "./chunk-HFNDKYVF.js";

// plots/matrix/hierCluster.renderers.js
var hierCluster_renderers_exports = {};
__export(hierCluster_renderers_exports, {
  maySetSandboxHeader: () => maySetSandboxHeader,
  plotDendrogramHclust: () => plotDendrogramHclust,
  renderImage: () => renderImage
});
function maySetSandboxHeader(appState) {
  if (!this.dom.header) return;
  switch (this.config.dataType) {
    case TermTypes.GENE_EXPRESSION:
      const headerText = this.config?.headerText ? `${this.config.headerText} ` : "";
      this.dom.header.text(`${headerText}Gene Expression Clustering`);
      break;
    case TermTypes.METABOLITE_INTENSITY:
      this.dom.header.text("Metabolite Intensity Clustering");
      break;
    case TermTypes.SSGSEA:
      this.dom.header.text("ssGSEA Clustering");
      break;
    case NUMERIC_DICTIONARY_TERM:
      this.dom.header.text(
        this.config.preBuiltPlotTitle ? this.config.preBuiltPlotTitle : appState.termdbConfig.numericDictTermCluster?.appName ? appState.termdbConfig.numericDictTermCluster.appName + " Clustering" : "Numeric Dictionary Term Clustering"
      );
      break;
    case TermTypes.PROTEOME_ABUNDANCE:
      this.dom.header.text(
        this.config.assayCohortTitle ? `Protein Abundance Clustering (${this.config.assayCohortTitle})` : "Protein Abundance Clustering"
      );
      break;
    default:
      throw `dataType '${this.config.dataType}' not recognized`;
  }
}
function plotDendrogramHclust(plotOnly) {
  const d = this.dimensions;
  const s = this.config.settings.matrix;
  const xOffset = d.seriesXoffset;
  const pxr = window.devicePixelRatio <= 1 ? 1 : window.devicePixelRatio;
  const obj = this.hierClusterData.clustering;
  const row = obj.row;
  const col = obj.col;
  const rowHeight = this.settings.matrix.clusterRowh, { xDendrogramHeight, yDendrogramHeight } = this.settings.hierCluster, colWidth = this.dimensions.dx;
  if (plotOnly !== "left") {
    if (!this.settings.hierCluster.clusterSamples) {
      this.dom.topDendrogram.selectAll("*").remove();
    } else {
      const height2px = getHclustHeightScalefactor(col.height, yDendrogramHeight);
      const height = yDendrogramHeight + 1e-7;
      const width = Math.min(colWidth * col.inputOrder.length, s.imgWMax);
      if (width <= 0 || height <= 0) {
        console.warn(
          "Skipping top dendrogram render: invalid dimensions.",
          "This may indicate a zoom feedback loop issue.",
          {
            width,
            height,
            colWidth,
            sampleCount: col.inputOrder.length,
            yDendrogramHeight
          }
        );
        this.dom.topDendrogram.selectAll("*").remove();
        return;
      }
      const canvas = new OffscreenCanvas(width * pxr, height * pxr);
      const ctx = canvas.getContext("2d");
      ctx.scale(pxr, pxr);
      ctx.translate(-d.xMin, 0);
      ctx.imageSmoothingEnabled = false;
      ctx.imageSmoothingQuality = "high";
      ctx.strokeStyle = "black";
      const mergedClusters = /* @__PURE__ */ new Map();
      for (const [clusterid0, pair] of col.merge.entries()) {
        const clusterid = clusterid0 + 1;
        const children = [];
        const childrenClusters = [];
        let x1, x2, y1, y2;
        if (pair.n1 < 0) {
          const [name, columnNumber] = getLeafNumber(pair.n1, col.inputOrder, col.order);
          x1 = colWidth * (columnNumber + 0.5);
          y1 = yDendrogramHeight;
          children.push({ name });
        } else {
          if (!mergedClusters.has(pair.n1)) throw "pair.n1 is positive but not seen before";
          const c = mergedClusters.get(pair.n1);
          x1 = c.x;
          y1 = c.y;
          children.push(...c.children);
          childrenClusters.push(pair.n1);
        }
        if (pair.n2 < 0) {
          const [name, columnNumber] = getLeafNumber(pair.n2, col.inputOrder, col.order);
          x2 = colWidth * (columnNumber + 0.5);
          y2 = yDendrogramHeight;
          children.push({ name });
        } else {
          if (!mergedClusters.has(pair.n2)) throw "pair.n1 is positive but not seen before";
          const c = mergedClusters.get(pair.n2);
          x2 = c.x;
          y2 = c.y;
          children.push(...c.children);
          childrenClusters.push(pair.n2);
        }
        const clusterY = yDendrogramHeight - col.height[clusterid0].height * height2px;
        const highlight = this.clickedClusterIds?.includes(clusterid);
        ctx.strokeStyle = highlight ? "red" : "black";
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x1, clusterY);
        ctx.lineTo(x2, clusterY);
        ctx.lineTo(x2, y2);
        ctx.stroke();
        ctx.closePath();
        mergedClusters.set(clusterid, {
          x: (x1 + x2) / 2,
          y: clusterY,
          children,
          childrenClusters,
          clusterPosition: {
            x1,
            x2,
            y1,
            y2,
            clusterY
          }
        });
      }
      this.renderImage(
        this.api,
        this.dom.topDendrogram,
        canvas,
        width,
        height,
        xDendrogramHeight + 0.5 * colWidth + d.xMin,
        s.margin.top + s.scrollHeight
      );
      col.mergedClusters = mergedClusters;
    }
  }
  if (plotOnly !== "top") {
    if (!this.settings.hierCluster.clusterRows) {
      this.dom.leftDendrogram.selectAll("*").remove();
    } else {
      const height2px = getHclustHeightScalefactor(row.height, xDendrogramHeight);
      const width = xDendrogramHeight + 1e-7;
      const height = rowHeight * row.inputOrder.length;
      const canvasWidthPx = Number.isFinite(width) && Number.isFinite(pxr) ? Math.max(0, Math.floor(width * pxr)) : 0;
      const canvasHeightPx = Number.isFinite(height) && Number.isFinite(pxr) ? Math.max(0, Math.floor(height * pxr)) : 0;
      if (!Number.isFinite(width) || !Number.isFinite(height) || !Number.isFinite(pxr) || width <= 0 || height <= 0 || pxr <= 0 || canvasWidthPx < 1 || canvasHeightPx < 1) {
        console.warn(
          "Skipping left dendrogram render: invalid dimensions.",
          "This may indicate a zoom feedback loop issue.",
          {
            width,
            height,
            pxr,
            canvasWidthPx,
            canvasHeightPx,
            rowHeight,
            termCount: row.inputOrder.length,
            xDendrogramHeight
          }
        );
        this.dom.leftDendrogram.selectAll("*").remove();
        return;
      }
      const canvas = new OffscreenCanvas(canvasWidthPx, canvasHeightPx);
      const ctx = canvas.getContext("2d");
      ctx.scale(pxr, pxr);
      ctx.imageSmoothingEnabled = false;
      ctx.imageSmoothingQuality = "high";
      ctx.strokeStyle = "black";
      const mergedClusters = /* @__PURE__ */ new Map();
      for (const [clusterid0, pair] of row.merge.entries()) {
        const clusterid = clusterid0 + 1;
        const children = [];
        const childrenClusters = [];
        let x1, x2, y1, y2;
        if (pair.n1 < 0) {
          const [name, rowNumber] = getLeafNumber(pair.n1, row.inputOrder, row.order);
          y1 = rowHeight * (rowNumber + 0.5);
          x1 = xDendrogramHeight;
          children.push({ name });
        } else {
          if (!mergedClusters.has(pair.n1)) throw "pair.n1 is positive but not seen before";
          const c = mergedClusters.get(pair.n1);
          x1 = c.x;
          y1 = c.y;
          children.push(...c.children);
          childrenClusters.push(pair.n1);
        }
        if (pair.n2 < 0) {
          const [name, rowNumber] = getLeafNumber(pair.n2, row.inputOrder, row.order);
          y2 = rowHeight * (rowNumber + 0.5);
          x2 = xDendrogramHeight;
          children.push({ name });
        } else {
          if (!mergedClusters.has(pair.n2)) throw "pair.n1 is positive but not seen before";
          const c = mergedClusters.get(pair.n2);
          x2 = c.x;
          y2 = c.y;
          children.push(...c.children);
          childrenClusters.push(pair.n2);
        }
        const clusterX = xDendrogramHeight - row.height[clusterid0].height * height2px;
        const highlight = this.clickedLeftClusterIds?.includes(clusterid);
        ctx.strokeStyle = highlight ? "red" : "black";
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(clusterX, y1);
        ctx.lineTo(clusterX, y2);
        ctx.lineTo(x2, y2);
        ctx.stroke();
        ctx.closePath();
        mergedClusters.set(clusterid, {
          x: clusterX,
          y: (y1 + y2) / 2,
          children,
          childrenClusters,
          clusterPosition: {
            x1,
            x2,
            y1,
            y2,
            clusterX
          }
        });
      }
      const t = this.termOrder.find((t2) => t2.grp.type == "hierCluster" || t2.grp.name == this.hcTermGroup.name);
      const y = (
        // t.labelOffset is commented out because it is already handled in adjustSvgDimensions
        t.grpIndex * s.rowgspace + t.prevGrpTotalIndex * s.rowh + t.totalHtAdjustments + s.margin.top + s.scrollHeight + // left dendrogram image must be lower than the top dendrogram image height
        yDendrogramHeight
      );
      this.renderImage(this.api, this.dom.leftDendrogram, canvas, width, height, 0, y);
      row.mergedClusters = mergedClusters;
    }
  }
}
async function renderImage(componentApi, g, canvas, width, height, x, y) {
  const sequenceId = componentApi.getSequenceId();
  const reader = new FileReader();
  reader.addEventListener(
    "load",
    () => {
      if (componentApi.isStaleSequenceId(sequenceId)) return;
      g.selectAll("*").remove();
      g.append("image").attr("x", x + 0.033).attr("y", y + 0.033).attr("xlink:href", reader.result).attr("width", width).attr("height", height);
    },
    false
  );
  const blob = await canvas.convertToBlob({ quality: 1 });
  reader.readAsDataURL(blob);
}
function getHclustHeightScalefactor(lst, ph) {
  let max = lst[0].height;
  for (const h of lst) max = Math.max(max, h.height);
  return ph / max;
}
function getLeafNumber(minus, inputOrder, order) {
  const name = inputOrder[-minus - 1];
  if (!name) throw "minus not in inputOrder";
  const i = order.findIndex((j) => j.name == name);
  if (i == -1) throw "name not found in hc$order";
  return [name, i];
}

export {
  maySetSandboxHeader,
  plotDendrogramHclust,
  renderImage,
  hierCluster_renderers_exports
};
//# sourceMappingURL=chunk-SHWLROJG.js.map
