import {
  getMclassSorter,
  getSampleGroupSorter,
  getSampleSorter,
  getTermSorter
} from "./chunk-GNIK4JYM.js";
import {
  sample_match_termvaluesetting
} from "./chunk-7IYJZZQI.js";
import {
  dtcnv,
  dtfusionrna,
  dtgeneexpression,
  dtsnvindel
} from "./chunk-4UWS5Y3N.js";
import {
  __export
} from "./chunk-HFNDKYVF.js";

// plots/matrix/matrix.groups.js
var matrix_groups_exports = {};
__export(matrix_groups_exports, {
  classifyValues: () => classifyValues,
  getSampleGroups: () => getSampleGroups,
  getSampleOrder: () => getSampleOrder,
  getTermOrder: () => getTermOrder,
  stackSiblingCellsByClass: () => stackSiblingCellsByClass
});
function getTermOrder(data) {
  const s = this.settings.matrix;
  this.termSorter = getTermSorter(this, s);
  const termOrder = [];
  let totalIndex = 0, visibleGrpIndex = 0, numClusterTerms = 0;
  this.mclassSorter = getMclassSorter(this);
  for (const [grpIndex, grp] of this.termGroups.entries()) {
    const lst = [];
    for (const [index, tw] of grp.lst.entries()) {
      const counts = { samples: 0, hits: 0 };
      const countedSamples = /* @__PURE__ */ new Set();
      for (const sd of data.lst) {
        if (countedSamples.has(sd.sample)) continue;
        countedSamples.add(sd.sample);
        const anno = sd[tw.$id];
        if (anno) {
          const { filteredValues, countedValues, renderedValues } = this.classifyValues(anno, tw, grp, s, sd);
          anno.filteredValues = filteredValues;
          anno.countedValues = countedValues;
          anno.renderedValues = renderedValues;
          if (anno.countedValues?.length) {
            const v = tw.term.values?.[anno.value];
            if (v?.uncountable) continue;
            counts.samples += 1;
            counts.hits += anno.countedValues.length;
            if (tw.q?.mode == "continuous") {
              const v2 = anno.value;
              if (!("minval" in counts) || counts.minval > v2) counts.minval = v2;
              if (!("maxval" in counts) || counts.maxval < v2) counts.maxval = v2;
            }
          }
        }
      }
      if (grp.type != "hierCluster" || counts.samples) lst.push({ tw, counts, index });
      if (grp.type == "hierCluster") numClusterTerms++;
    }
    const termSorter = grp.sortTermsBy || grp.type == "hierCluster" ? getTermSorter(this, s, grp) : this.termSorter;
    const processedLst = lst.filter((t) => {
      if ("minNumSamples" in t.tw) return t.tw.minNumSamples <= t.counts.samples;
      if (!grp.settings) return true;
      return !("minNumSamples" in grp.settings) || t.counts.samples >= grp.settings.minNumSamples;
    }).sort(termSorter);
    if (!processedLst.length) continue;
    for (const [index, t] of processedLst.entries()) {
      const { tw, counts } = t;
      const ref = data.refs.byTermId[t.tw.$id] || {};
      termOrder.push({
        grp,
        grpIndex,
        visibleGrpIndex,
        tw,
        index,
        // rendered index
        lstIndex: t.index,
        // as-listed index, before applying term filters
        processedLst,
        prevGrpTotalIndex: totalIndex,
        totalIndex: totalIndex + index,
        ref,
        allCounts: counts
        // note: term label will be assigned after sample counts are known
        // label: t.tw.label || t.tw.term.name,
      });
    }
    totalIndex += processedLst.length;
    visibleGrpIndex += 1;
  }
  this.numTerms = termOrder.length;
  this.numClusterTerms = numClusterTerms;
  return termOrder;
}
function getSampleGroups(data) {
  const s = this.settings.matrix;
  const defaultSampleGrp = {
    id: this.config.divideBy?.$id,
    name: this.config.divideBy ? "Not annotated" : "",
    lst: []
  };
  const sampleGroups = /* @__PURE__ */ new Map();
  const term = this.config.divideBy?.term || {};
  const $id = this.config.divideBy?.$id || "-";
  const exclude = this.config.divideBy?.exclude || [];
  const values = term.values || {};
  const ref = data.refs.byTermId[$id] || {};
  for (const row of data.lst) {
    if ($id in row) {
      const key = row[$id].key;
      const name = key in values && values[key].label ? values[key].label : key;
      if (!sampleGroups.has(key)) {
        const grp = {
          name: `${name}`,
          // convert to a string
          id: key,
          lst: [],
          tw: this.config.divideBy,
          legendGroups: {},
          isExcluded: exclude.includes(key)
        };
        if (ref.bins && s.sortSampleGrpsBy == "name") grp.order = ref.bins.findIndex((bin) => bin.name == key);
        else delete grp.order;
        sampleGroups.set(key, grp);
      }
      sampleGroups.get(key).lst.push(row);
    } else {
      defaultSampleGrp.lst.push(row);
    }
  }
  const sampleGrpsArr = [...sampleGroups.values()];
  const n = sampleGroups.size;
  if (n > 100 && sampleGrpsArr.filter((sg) => sg.lst.length < 3).length > 0.8 * n) {
    const l = s.controlLabels;
    throw `Did not group ${l.samples} by "${term.name}": too many ${l.sample} groups (${n}), with the majority of groups having <= 2 ${l.samples} per group.`;
  }
  if (defaultSampleGrp.lst.length && !sampleGroups.size) {
    sampleGroups.set(void 0, defaultSampleGrp);
    sampleGrpsArr.push(...sampleGroups.values());
  }
  this.asListedSampleOrder = [];
  for (const grp of sampleGrpsArr) {
    this.asListedSampleOrder.push(...grp.lst.map((s2) => s2.sample));
  }
  const selectedDictTerms = this.termOrder.filter((t) => t.tw.sortSamples && t.tw.term.type != "geneVariant");
  const noGrpSampleSorter = getSampleSorter(this, s, data.lst, {
    skipSorter: (p, tw) => !p.types?.includes("geneVariant") && selectedDictTerms.find((t) => t.tw.$id === tw.$id)
  });
  const noGrpSampleOrder = data.lst.sort(noGrpSampleSorter);
  const allowedSamples = noGrpSampleOrder.slice(0, s.maxSample);
  const dataFilter = (d) => allowedSamples.includes(d);
  const hitsPerSample = (t, c) => t + (typeof c == "object" && c.countedValues?.length ? 1 : 0);
  const countHits = (total, d) => total + (Object.values(d).reduce(hitsPerSample, 0) ? 1 : 0);
  const grpLstSampleSorter = getSampleSorter(this, s, data.lst);
  for (const grp of sampleGrpsArr) {
    grp.lst = grp.lst.filter(dataFilter);
    grp.totalCountedValues = grp.lst.reduce(countHits, 0);
    grp.lst.sort(grpLstSampleSorter);
  }
  const sampleGrpSorter = getSampleGroupSorter(this);
  return sampleGrpsArr.sort(sampleGrpSorter);
}
function getSampleOrder(data) {
  const s = this.settings.matrix;
  this.visibleSampleGrps = /* @__PURE__ */ new Set();
  const sampleOrder = [];
  let total = 0, numHiddenGrps = 0;
  for (const [grpIndex, grp] of this.sampleGroups.entries()) {
    if (!grp.lst.length) continue;
    if (grp.isExcluded) numHiddenGrps++;
    let processedLst = grp.lst;
    for (const [index, row] of processedLst.entries()) {
      sampleOrder.push({
        grp,
        grpIndex: grpIndex - numHiddenGrps,
        // : this.sampleGroups.length,
        row,
        index,
        prevGrpTotalIndex: total,
        totalIndex: total + index,
        totalHtAdjustments: 0,
        // may be required when transposed???
        grpTotals: { htAdjustment: 0 },
        // may be required when transposed???
        processedLst
      });
    }
    if (!grp.isExcluded) total += processedLst.length;
    this.visibleSampleGrps.add(grp);
  }
  this.unfilteredSampleOrder = sampleOrder;
  return sampleOrder.filter((so) => !so.grp.isExcluded);
}
function classifyValues(anno, tw, grp, s, sample) {
  const values = "value" in anno ? [anno.value] : anno.values;
  if (!values) return { filteredValues: null, countedValues: null, renderedValues: null };
  const isSpecific = [tw.valueFilter || grp.valueFilter].filter((v) => v && true);
  if (isSpecific.length && isSpecific[0].type !== "tvs" && isSpecific[0].type !== "tvslst")
    throw `unknown matrix value filter type='${isSpecific.type}'`;
  let filteredValues = !isSpecific.length ? values : values.filter((v) => sample_match_termvaluesetting(v, isSpecific[0], tw.term, sample));
  const renderedValues = [];
  if (tw.term.type == "geneVariant" && tw.q?.type == "values") {
    filteredValues.sort(this.mclassSorter);
    if (s.cellEncoding == "") renderedValues.push(...filteredValues);
    else {
      const sortedFilteredValues = [];
      for (const dt of [dtcnv, dtsnvindel, dtfusionrna, dtgeneexpression]) {
        const v = dt == dtgeneexpression ? filteredValues.find((v2) => v2.dt === dt) : filteredValues.find((v2) => v2.dt === dt && v2.class !== "WT" && v2.class !== "Blank");
        if (v) renderedValues.push(v);
        const oneDtV = filteredValues.filter((v2) => v2.dt === dt);
        sortedFilteredValues.push(...oneDtV);
      }
      filteredValues = sortedFilteredValues;
    }
  } else {
    renderedValues.push(...filteredValues);
  }
  return {
    filteredValues,
    countedValues: filteredValues.filter((v) => {
      if (tw.term.type == "geneVariant") {
        if (tw.q?.type == "predefined-groupset" || tw.q?.type == "custom-groupset") {
          const groupset = tw.q.type == "predefined-groupset" ? tw.term.groupsetting.lst[tw.q.predefined_groupset_idx] : tw.q.customset;
          if (!groupset) throw "groupset not found";
          const group = groupset.groups[0];
          if (v != group.name) return false;
        } else {
          if (v.class == "WT" || v.class == "Blank" || s.geneVariantCountSamplesSkipMclass.includes(v.class))
            return false;
        }
      }
      return true;
    }),
    renderedValues
  };
}
function stackSiblingCellsByClass(a, b) {
  return a.class === b.class ? 0 : a.class === "Blank" ? 1 : b.class == "Blank" ? -1 : a.class < b.class ? -1 : 1;
}

export {
  getTermOrder,
  getSampleGroups,
  getSampleOrder,
  classifyValues,
  stackSiblingCellsByClass,
  matrix_groups_exports
};
//# sourceMappingURL=chunk-ODG6RD7P.js.map
