import {
  getDefaultViolinSettings
} from "./chunk-FFEIP5CT.js";
import {
  getDefaultScatterSettings
} from "./chunk-OEDZHPBJ.js";
import {
  getDefaultBoxplotSettings
} from "./chunk-PCZNTK74.js";
import {
  PlotBase,
  defaultUiLabels,
  fillTermWrapper,
  filterRxCompInit,
  getDefaultBarSettings,
  term0_term2_defaultQ
} from "./chunk-SA7APTJR.js";
import {
  importPlot
} from "./chunk-SKREEF3H.js";
import {
  Menu
} from "./chunk-HYOEWQ5P.js";
import {
  Tabs
} from "./chunk-HBW42TDT.js";
import {
  copyMerge,
  getCompInit
} from "./chunk-M3J4MINX.js";
import {
  isNumericTerm
} from "./chunk-NOEAT6CX.js";

// plots/summary.ts
var SummaryPlot = class _SummaryPlot extends PlotBase {
  constructor(opts, api) {
    super(opts, api);
    this.components = {};
    // expected class-specific props
    this.chartsByType = {};
    this.configTermKeys = ["term", "term0", "term2"];
    this.type = _SummaryPlot.type;
    this.dom = this.getDom(opts);
    this.tabsData = this.getTabsData();
    this.chartToggles = new Tabs({ holder: this.dom.chartToggles, tabs: this.tabsData, noContent: true });
    this.components = {
      plots: {}
    };
  }
  static {
    this.type = "summary";
  }
  preApiFreeze(api) {
    api.getError = () => {
      return this.error;
    };
  }
  getDom(opts) {
    const holder = opts.holder;
    holder.header.style("padding", 0);
    const paneTitleDiv = holder.header.append("div").style("display", "inline-block").style("color", "#555").style("padding-left", "7px");
    return {
      tip: new Menu({ padding: "0px" }),
      holder,
      body: holder.body.style("white-space", "nowrap").style("overflow-x", "auto"),
      // will hold no data notice or the page title in multichart views
      errdiv: holder.body.append("div").attr("class", "sja_errorbar").style("display", "none").style("padding", "5px"),
      loading: holder.body.append("div").style("display", "none").style("padding", "5px").html("Loading ..."),
      // dom.viz will hold the rendered view
      viz: holder.body.append("div"),
      plotDivs: {},
      paneTitleDiv,
      paneTitleText: paneTitleDiv.append("div").classed("sjpp-term-header", true).style("display", "inline-block").style("vertical-align", "sub"),
      chartToggles: paneTitleDiv.append("div").style("display", "inline-block").style("margin-left", "10px"),
      localRecoverDiv: paneTitleDiv.append("div").style("display", "inline-block"),
      filterDiv: holder.header.append("div").style("display", "inline-block").style("zoom", 0.9)
    };
  }
  getTabsData() {
    const callback = (event, tab) => this.tabClickCallback(event, tab);
    return [
      {
        childType: "barchart",
        label: "Barchart",
        isVisible: () => true,
        disabled: () => false,
        getConfig: async () => {
          if (!this.config) return;
          const config = { id: this.id, childType: "barchart" };
          const { term, term2 } = this.config || {};
          if (term) {
            const mode = isNumericTerm(term?.term) ? "discrete" : term?.q.mode || "discrete";
            config.term = await this.getWrappedTermCopy(term, mode);
          }
          if (term2) {
            const mode = isNumericTerm(term2.term) ? "discrete" : term2.q.mode || "discrete";
            config.term2 = await this.getWrappedTermCopy(term2, mode);
          }
          return config;
        },
        active: true,
        callback
      },
      {
        childType: "violin",
        label: "Violin",
        disabled: () => false,
        isVisible: () => isNumericTerm(this.config?.term?.term) || isNumericTerm(this.config?.term2?.term),
        getConfig: async () => {
          const term = this.config?.term;
          const term2 = this.config.term2;
          let _term, _term2;
          this.violinContTerm = isNumericTerm(term?.term) ? "term" : "term2";
          if (this.violinContTerm && this.violinContTerm === "term" || term.q?.mode == "continuous") {
            _term = await this.getWrappedTermCopy(term, "continuous");
            _term2 = await this.getWrappedTermCopy(term2, "discrete");
            this.violinContTerm = "term";
          } else if (this.violinContTerm && this.violinContTerm === "term2" || term2?.q?.mode == "continuous") {
            _term = await this.getWrappedTermCopy(term, "discrete");
            _term2 = await this.getWrappedTermCopy(term2, "continuous");
            this.violinContTerm = "term2";
          } else if (term2?.q?.mode == "discrete") {
            _term = await this.getWrappedTermCopy(term, "discrete");
            _term2 = await this.getWrappedTermCopy(term2, "continuous");
            this.violinContTerm = "term2";
          } else {
            _term = await this.getWrappedTermCopy(term, "continuous");
            _term2 = await this.getWrappedTermCopy(term2, "discrete");
            this.violinContTerm = "term";
          }
          const config = { childType: "violin", term: _term, term2: _term2 };
          return config;
        },
        active: false,
        callback
      },
      {
        childType: "boxplot",
        label: "Boxplot",
        disabled: () => false,
        isVisible: () => isNumericTerm(this.config?.term?.term) || isNumericTerm(this.config?.term2?.term),
        getConfig: async () => {
          const _term = this.config?.term;
          const _term2 = this.config.term2;
          let termMode = "continuous", term2Mode = "discrete";
          this.boxContTerm = isNumericTerm(_term?.term) ? "term" : "term2";
          if (this.boxContTerm && this.boxContTerm === "term" || _term.q?.mode == "continuous") {
            termMode = "continuous";
            term2Mode = "discrete";
            this.boxContTerm = "term";
          } else if (this.boxContTerm && this.boxContTerm === "term2" || _term2?.q?.mode == "continuous") {
            termMode = "discrete";
            term2Mode = "continuous";
            this.boxContTerm = "term2";
          } else if (_term2?.q?.mode == "discrete") {
            termMode = "discrete";
            term2Mode = "continuous";
            this.boxContTerm = "term2";
          }
          const term = await this.getWrappedTermCopy(_term, termMode);
          const term2 = await this.getWrappedTermCopy(_term2, term2Mode);
          const config = {
            childType: "boxplot",
            term,
            term2
          };
          return config;
        },
        active: false,
        callback
      },
      {
        childType: "sampleScatter",
        label: "Scatter",
        disabled: () => false,
        isVisible: () => {
          return isNumericTerm(this.config?.term.term) && isNumericTerm(this.config?.term2?.term);
        },
        getConfig: async () => {
          const _term = await this.getWrappedTermCopy(this.config?.term, "continuous");
          const _term2 = await this.getWrappedTermCopy(this.config?.term2, "continuous");
          const config = {
            childType: "sampleScatter",
            term: _term,
            term2: _term2,
            groups: [],
            term0: this.config.term0
          };
          return config;
        },
        active: false,
        callback
      }
    ];
  }
  async init(appState) {
    const state = this.getState(appState);
    const config = structuredClone(state.config);
    this.initUi(this.opts, config);
    this.components.filter = await filterRxCompInit({
      app: this.app,
      vocabApi: this.app.vocabApi,
      parentId: this.id,
      holder: this.dom.filterDiv,
      hideLabel: true,
      emptyLabel: "+Add new filter",
      callback: (filter) => {
        this.app.dispatch({
          id: this.id,
          type: "plot_edit",
          config: { filter }
        });
      }
    });
  }
  reactsTo(action) {
    if (action.type.includes("cache_termq")) return true;
    if (action.type.startsWith("plot_")) {
      if (action.type.startsWith("plot_")) {
        return action.id === this.id || action.id == this.parentId;
      }
    }
    if (action.type.startsWith("filter")) return true;
    if (action.type.startsWith("cohort")) return true;
    if (action.type == "app_refresh") return true;
    return false;
  }
  getState(appState) {
    const config = appState.plots.find((p) => p.id === this.id);
    if (!config) {
      throw `No plot with id='${this.id}' found. Did you set this.id before this.api = getComponentApi(this)?`;
    }
    return {
      termfilter: appState.termfilter,
      config,
      // quick fix to skip history tracking as needed
      _scope_: appState._scope_
    };
  }
  async main() {
    delete this.error;
    this.dom.errdiv.style("display", "none").html("");
    this.dom.loading.style("display", "");
    this.config = await this.getMutableConfig();
    this.maySetSandboxHeader();
    if (!this.components.plots[this.config.childType]) {
      await this.setComponent(this.config);
    }
    this.dom.loading.style("display", "none");
    this.render();
    const activeTabIndex = this.tabsData.findIndex((tab) => tab.childType == this.config.childType);
    this.chartToggles.update(activeTabIndex);
    const numVisTabs = this.tabsData.filter((d) => d.isVisible()).length;
    if (numVisTabs > 1) this.dom.chartToggles.style("display", "inline-block");
    else this.dom.chartToggles.style("display", "none");
  }
  maySetSandboxHeader() {
    const { term, term2, headerText, assayCohortTitle } = this.config;
    const mainTerm = term.getTitleText?.() || term.term.name;
    const _headerPretext = headerText ? `${headerText} ` : "";
    const _headerSuffix = assayCohortTitle ? ` (${assayCohortTitle})` : "";
    const titleBase = mainTerm + _headerSuffix;
    if (term2?.type) {
      this.dom.paneTitleText.html(`${_headerPretext}${term2.getTitleText?.() || term2.term.name} vs ${titleBase}`);
    } else {
      this.dom.paneTitleText.html(`${_headerPretext}${titleBase}`);
    }
  }
  async setComponent(config) {
    const _ = await importPlot(config.childType, `unsupported childType='${config.childType}'`);
    this.dom.plotDivs[config.childType] = this.dom.viz.append("div");
    this.components.plots[config.childType] = await _.componentInit({
      app: this.app,
      holder: this.dom.plotDivs[config.childType],
      id: this.id,
      parent: this.api
    });
  }
  destroy() {
    this.dom.holder.app_div.selectAll("*").remove();
    this.dom.holder.app_div.remove();
    for (const key in this.dom) {
      delete this.dom[key];
    }
  }
  initUi(opts, config) {
    this.dom.paneTitleText.html(config.term.term.name);
    this.chartToggles.main();
  }
  async tabClickCallback(event, tab) {
    if (!tab || !tab.getConfig) return;
    let config;
    try {
      this.dom.viz.style("display", "none");
      this.dom.loading.style("display", "");
      config = await tab.getConfig();
      this.dom.loading.style("display", "none");
      this.dom.viz.style("display", "");
      if (config)
        this.app.dispatch({
          type: "plot_edit",
          id: this.id,
          config
        });
    } catch (e) {
      this.dom.loading.style("display", "none");
      this.error = e;
      this.dom.errdiv.style("display", "").html(`Error: ${e.message || e.error || e}`);
    }
  }
  async getWrappedTermCopy(term, mode) {
    if (!term) return;
    const tw = structuredClone(term);
    tw.q.mode = mode;
    await fillTermWrapper(tw, this.app.vocabApi);
    return tw;
  }
  render() {
    for (const childType of Object.keys(this.components.plots)) {
      const chart = this.components.plots[childType];
      if (chart.type != this.config.childType) {
        this.dom.plotDivs[chart.type].style("display", "none");
      }
    }
    this.dom.plotDivs[this.config.childType].style("display", "");
  }
  getChartImages() {
    const chart = this.components.plots[this.config.childType];
    if (chart?.getChartImages) {
      return chart.getChartImages();
    }
    return null;
  }
};
var summaryInit = getCompInit(SummaryPlot);
var componentInit = summaryInit;
async function getPlotConfig(opts, app) {
  if (!opts.term) throw "summary getPlotConfig: opts.term{} missing";
  try {
    opts.term = await fillTermWrapper(
      opts.term,
      app.vocabApi,
      opts.term.q || { geneVariant: { type: "predefined-groupset" } }
    );
    if (opts.term2)
      opts.term2 = await fillTermWrapper(
        opts.term2,
        app.vocabApi,
        opts.term2.bins || opts.term2.q ? void 0 : term0_term2_defaultQ
      );
    if (opts.term0)
      opts.term0 = await fillTermWrapper(
        opts.term0,
        app.vocabApi,
        opts.term0.bins || opts.term0.q ? void 0 : term0_term2_defaultQ
      );
    if (opts.colorTW) opts.colorTW = await fillTermWrapper(opts.colorTW, app.vocabApi);
    if (opts.shapeTW) opts.shapeTW = await fillTermWrapper(opts.shapeTW, app.vocabApi);
    if (opts.scaleDotTW) opts.scaleDotTW = await fillTermWrapper(opts.scaleDotTW, app.vocabApi);
  } catch (e) {
    if (e.stack) console.log(e.stack);
    throw `${e} [summary getPlotConfig()]`;
  }
  const config = {
    chartType: "summary",
    childType: "barchart",
    term: opts.term,
    groups: [],
    controlLabels: Object.assign({}, defaultUiLabels, app.vocabApi.termdbConfig.uiLabels || {}),
    settings: {
      controls: {
        isOpen: false
      },
      common: {
        use_logscale: false,
        // flag for y-axis scale type, 0=linear, 1=log
        use_percentage: false
        // Move to barchart settings
        // barheight: 300, // maximum bar length
        // barwidth: 20, // bar thickness
        // barspace: 2 // space between two bars
      },
      barchart: getDefaultBarSettings(app),
      violin: getDefaultViolinSettings(app),
      boxplot: getDefaultBoxplotSettings(app),
      sampleScatter: getDefaultScatterSettings(opts)
    },
    mayAdjustConfig(config2, edits = {}) {
      return mayAdjustConfig(config2, opts, edits);
    }
  };
  if (config.term.term.type == "geneVariant") config.settings.barchart.colorBars = true;
  copyMerge(config, opts);
  Object.assign(config.controlLabels, app.vocabApi.termdbConfig.uiLabels || {});
  return config;
}
var discreteByContinuousPlots = /* @__PURE__ */ new Set(["violin", "boxplot"]);
function mayAdjustConfig(config, opts, edits = {}) {
  if (edits.childType) {
    if (config.childType != edits.childType) throw `action.config.childType was not applied in mass store.plot_edit()`;
    return;
  }
  if (config.term?.q?.mode == "continuous" && config.term2?.q?.mode == "continuous") {
    config.childType = "sampleScatter";
  } else if (config.term?.term?.type == "termCollection") {
    if (config.term.term.memberType == "categorical") {
      config.childType = "barchart";
    } else if (config.term.term.memberType == "numeric") {
      if (config.childType) {
        if (config.childType == "barchart") {
          config.childType = "violin";
        } else {
        }
      } else {
        config.childType = "violin";
      }
    } else {
      throw new Error("config.term.term.memberType not categorical or numeric");
    }
  } else if (config.term?.q?.mode == "continuous" || config.term2?.q?.mode == "continuous") {
    if (!discreteByContinuousPlots.has(config.childType)) {
      if (opts.childType && !discreteByContinuousPlots.has(opts.childType)) {
        console.warn(
          `ignoring summary opts.childType='${opts.childType}' since it does not support plotting discrete by continuous tw's`
        );
        config.childType = "violin";
      } else {
        config.childType = opts.childType || "violin";
      }
    }
  } else {
    config.childType = "barchart";
  }
}

export {
  summaryInit,
  componentInit,
  getPlotConfig,
  mayAdjustConfig
};
//# sourceMappingURL=chunk-BDIKFVWZ.js.map
