import {
  keyupEnter
} from "./chunk-SA7APTJR.js";
import {
  get_bin_label,
  get_bin_range_equation
} from "./chunk-QH6W3NVF.js";

// termsetting/handlers/NumCustomBinEditor.ts
var NumCustomBinEditor = class {
  constructor(editHandler) {
    this.dom = {};
    this.editHandler = editHandler;
    this.opts = editHandler.opts;
    this.tw = editHandler.tw;
    this.termsetting = editHandler.termsetting;
  }
  async render(div) {
    this.tw = this.editHandler.tw;
    this.q = this.getDefaultQ();
    await this.editHandler.handler.density.setBinLines(this.getBoundaryOpts());
    if (this.dom.inputsDiv) {
      if (this.editHandler.dom.binsDiv?.node().contains(this.dom.inputsDiv.node())) return;
      else {
        this.dom.inputsDiv.remove();
        delete this.dom.inputsDiv;
      }
    }
    this.dom.inputsDiv = div.append("div").style("display", "flex").style("width", "100%");
    this.renderCustomBinInputs();
  }
  getBoundaryOpts() {
    return {
      values: this.q.lst.slice(1).map((bin) => ({ x: bin.startunbounded ? bin.stop : bin.start, isDraggable: true })),
      callback: (d, value) => {
        const boundaryValues = this.q.lst.slice(1).map((d2) => "start" in d2 ? d2.start : "");
        boundaryValues[d.index] = value;
        this.dom.customBinBoundaryInput.text(boundaryValues.join(",\n"));
        this.handleInputChange("drag");
        return 0;
      }
    };
  }
  getDefaultQ() {
    if (this.tw.q.mode == "discrete" && this.tw.q.type == "custom-bin") {
      const copy = JSON.parse(JSON.stringify(this.tw.q));
      copy.lst.forEach((bin) => {
        if (!bin.label) bin.label = get_bin_label(bin, this.tw.q, this.tw.term.valueConversion);
        bin.range = get_bin_range_equation(bin, this.tw.q);
      });
      return copy;
    }
    const { min, max } = this.editHandler.handler.density_data;
    const defaultCustomBoundary = (
      /* when no sample is annotated by this term,
      minvalue and maxvalue are both null
      setting defaultCustomBoundary to arbitrary "0" will allow existing UI to work
      but remains to be evaluated if is really okay to use 0
      */
      !Number.isFinite(min) || !Number.isFinite(max) ? 0 : (
        // minvalue and maxvalue is valid number
        max != min ? min + (max - min) / 2 : max
      )
    );
    const firstBin = {
      startunbounded: true,
      startinclusive: false,
      stopinclusive: false,
      stop: +defaultCustomBoundary.toFixed(this.tw.term.type == "integer" ? 0 : 2)
    };
    const lastBin = {
      stopunbounded: true,
      startinclusive: true,
      stopinclusive: false,
      start: +defaultCustomBoundary.toFixed(this.tw.term.type == "integer" ? 0 : 2)
    };
    return {
      mode: "discrete",
      type: "custom-bin",
      lst: [
        // Type '{ label: any; range: any; startunbounded: boolean; startinclusive: boolean; stopinclusive: boolean; stop: number; }' is not assignable to type 'StartUnboundedBin | FullyBoundedBin'.
        // Property 'start' is missing in type '{ label: any; range: any; startunbounded: boolean; startinclusive: boolean; stopinclusive: boolean; stop: number; }' but required in type 'FullyBoundedBin'.
        {
          ...firstBin,
          label: get_bin_label(firstBin, this.tw.q, this.tw.term.valueConversion),
          range: get_bin_range_equation(firstBin, this.tw.q)
        },
        {
          ...lastBin,
          label: get_bin_label(lastBin, this.tw.q, this.tw.term.valueConversion),
          range: get_bin_range_equation(lastBin, this.tw.q)
        }
        //satisfies StopUnboundedBin
      ]
    };
  }
  /******************* Functions for Numerical Custom size bins *******************/
  renderCustomBinInputs() {
    const q = this.q;
    const boundaryDiv = this.dom.inputsDiv.append("div").style("margin-right", "20px");
    this.dom.rangeAndLabelDiv = this.dom.inputsDiv.append("div");
    boundaryDiv.append("div").style("margin-bottom", "5px").style("color", "rgb(136, 136, 136)").text("Bin boundaries");
    this.dom.customBinBoundaryInput = boundaryDiv.append("textarea").style("width", "100px").style("height", "70px").text(
      q.lst.slice(1).map((d) => "start" in d ? d.start : "").join("\n")
    ).on("change", () => this.handleInputChange()).on("keyup", async (event) => {
      if (!keyupEnter(event) && event.key != 8 && event.key != "Enter") return;
      if (!this.dom.inputsDiv.selectAll("input").node().value) return;
      this.handleInputChange();
    });
    boundaryDiv.append("div").style("font-size", ".6em").style("margin-left", "1px").style("color", "#858585").html("Enter numeric values </br>seperated by ENTER");
    this.renderBoundaryInputDivs();
  }
  renderBoundaryInputDivs() {
    const data = this.q.lst;
    const holder = this.dom.rangeAndLabelDiv;
    holder.selectAll("*").remove();
    const grid = holder.append("div").style("display", "grid").style("grid-template-columns", "auto auto").style("column-gap", "20px").style("align-items", "center");
    grid.append("div").style("margin-bottom", "3px").style("color", "rgb(136, 136, 136)").text("Range");
    grid.append("div").style("margin-bottom", "3px").style("color", "rgb(136, 136, 136)").text("Bin label");
    for (const d of data) {
      grid.append("div").attr("name", "range").html(d.range);
      grid.append("div").append("input").attr("type", "text").style("margin", "2px 0px").property("value", d.label).on("change", function() {
        d.label = this.value;
      });
    }
    this.dom.customBinRanges = this.dom.inputsDiv.selectAll('div[name="range"]').data(data);
    this.dom.customBinLabelInput = this.dom.inputsDiv.selectAll("input").data(data);
  }
  handleInputChange(eventType = "") {
    const self = this.tw;
    const inputs = this.dom.inputsDiv.selectAll("input");
    inputs.property("value", "");
    const data = this.processCustomBinInputs();
    if (data == void 0) {
      return;
    }
    if (self.q.hiddenValues) this.tw.q.hiddenValues = self.q.hiddenValues;
    if (this.binsChanged(data, this.q.lst)) {
      this.q.lst = data;
    }
    this.renderBoundaryInputDivs();
    if (eventType != "drag") this.editHandler.handler.density.setBinLines(this.getBoundaryOpts());
  }
  binsChanged(data, qlst) {
    if (data.length != qlst.length) return true;
    if (Object.keys(data[0]).length !== Object.keys(qlst[0]).length) return true;
    for (const [i, bin] of qlst.entries()) {
      for (const k of Object.keys(bin)) {
        if (bin[k] && bin[k] !== data[i][k]) {
          return true;
        }
      }
    }
    return false;
  }
  processCustomBinInputs() {
    const self = this.termsetting;
    const startinclusive = this.editHandler.boundaryInclusion == "startinclusive";
    const stopinclusive = this.editHandler.boundaryInclusion == "stopinclusive";
    const inputs = this.dom.inputsDiv.node().querySelectorAll("input");
    const inputData = this.dom.customBinBoundaryInput.property("value").split("\n").filter((d) => d != "" && !isNaN(d));
    const trackBins = new Set(inputData);
    if (!trackBins.size) return this.tw.q.lst;
    const sortedBins = Array.from(trackBins).map(Number).sort((a, b) => a - b);
    const data = [
      // first bin: StartUnbounded type
      {
        startunbounded: true,
        stop: sortedBins[0],
        startinclusive: false,
        stopinclusive,
        label: inputs[0].value
      }
    ];
    if (!data[0].label) data[0].label = get_bin_label(data[0], self.q, self.term.valueConversion);
    if (!data[0].range) data[0].range = get_bin_range_equation(data[0], self.q);
    for (const [i, d] of sortedBins.entries()) {
      let bin;
      const label = inputs[i + 1]?.value || "";
      if (i !== trackBins.size - 1) {
        bin = {
          start: +d,
          startinclusive,
          stopinclusive,
          stop: sortedBins[i + 1],
          label
          //range: ''
        };
      } else {
        bin = {
          start: +d,
          startinclusive,
          stopinclusive: false,
          stopunbounded: true,
          label
          //range: ''
        };
      }
      if (bin.label === "" || bin.label === void 0) bin.label = get_bin_label(bin, self.q, self.term.valueConversion);
      if (bin.range === "" || bin.range === void 0) bin.range = get_bin_range_equation(bin, self.q);
      data.push(bin);
    }
    return data;
  }
  getEditedQ(destroyDom = true) {
    const lst = this.processCustomBinInputs();
    if (destroyDom) {
      for (const name of Object.keys(this.dom)) {
        this.dom[name].remove();
        delete this.dom[name];
      }
    }
    return {
      mode: "discrete",
      type: "custom-bin",
      lst
    };
  }
  undoEdits() {
    this.q = this.getDefaultQ();
    this.dom.inputsDiv.selectAll("*").remove();
    this.renderCustomBinInputs();
    this.editHandler.handler.density.setBinLines(this.getBoundaryOpts());
  }
};

export {
  NumCustomBinEditor
};
//# sourceMappingURL=chunk-NGM3ZLIP.js.map
