/* COPYRIGHT Esri - https://js.arcgis.com/1.2/LICENSE.txt */
import { jsPDF } from "jspdf";
import { g as getAssetPath } from "./runtime.js";
import { a as queryFeaturesByID } from "./queryUtils.js";
import { Sanitizer } from "@esri/arcgis-html-sanitizer";
import * as common from "@esri/solution-common";
import * as arcade from "@arcgis/core/arcade";
import * as intl from "@arcgis/core/intl";
function downloadCSVFile(title, outputLines) {
  const link = document.createElement("a");
  if (link.download !== void 0) {
    link.href = URL.createObjectURL(new Blob([decodeURIComponent("%ef%bb%bf"), outputLines.join("")], { type: "text/csv;charset=utf-8;" }));
    link.download = `${title}.csv` || "export.csv";
    link.style.visibility = "hidden";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
}
function exportCSV(filename, labels) {
  const outputLines = labels.map(
    (label) => Object.values(label).map((v) => `"${v}"`).join(",") + "\r\n"
  );
  downloadCSVFile(filename, outputLines);
}
var EPageType;
(function(EPageType2) {
  EPageType2["A4"] = "A4";
  EPageType2["ANSI_A"] = "ANSI A";
})(EPageType || (EPageType = {}));
class PDFCreator {
  // Properties are public for testing purposes
  dataPath = "";
  lang = "en";
  title = "";
  fontProps = {
    fontResolutionInchesPerPoint: 1 / 72,
    fontFullHeightRatio: 1.28
  };
  imageOptions = {
    x: 0,
    y: 0,
    width: 0,
    height: 0
    // inches
  };
  lineOptions = {
    x1: 0,
    y1: 0,
    x2: 0,
    y2: 0,
    lineProperties: {
      thickness: 0.1,
      color: "000000",
      opacity: 1
      // 0..1
    }
  };
  pageOptions = {
    pageType: EPageType.ANSI_A,
    width: 8.5,
    height: 11,
    leftMargin: 0.25,
    rightMargin: 0.25,
    topMargin: 0.25,
    bottomMargin: 0.25,
    drawNeatline: false
  };
  rectangleOptions = {
    left: 0,
    top: 0,
    width: 8.5,
    height: 11,
    lineProperties: {
      thickness: 0.1,
      color: "000000",
      opacity: 1
      // 0..1
    },
    fillColor: "",
    fillOpacity: 1
    // 0..1
  };
  tableOptions = {
    lineProperties: {
      thickness: 0.1,
      color: "",
      opacity: 1
      // 0..1
    },
    textProperties: {
      left: 0,
      top: 0,
      fontPoints: 1,
      fontColor: "000000"
      // i.e., black
    }
  };
  textOptions = {
    left: 0,
    top: 0,
    fontPoints: 1,
    fontColor: "000000"
    // i.e., black
  };
  //-- Static methods ------------------------------------------------------------------------------------------------//
  /**
   * @class PDFCreator
   */
  static getPageSize(pageType) {
    if (typeof pageType === "string") {
      pageType = pageType === EPageType.A4 ? EPageType.A4 : EPageType.ANSI_A;
    }
    switch (pageType) {
      case EPageType.A4:
        return {
          width: 8.25,
          height: 11.708333
        };
      case EPageType.ANSI_A:
        return {
          width: 8.5,
          height: 11
        };
    }
  }
  //-- Public methods ------------------------------------------------------------------------------------------------//
  /**
   * Adds a page to the document being created.
   *
   * @class PDFCreator
   */
  addPage() {
  }
  /**
   * Calculates a color intensity that emulates an opacity being applied to it.
   *
   * @param colorIntensity Color value to start with; range 0..1
   * @param opacity Opacity to apply; range 0..1
   *
   * @return Color intensity emulating the application of opacity. It works by applying the opacity value
   * to the input colorIntensity and adding the inverse of the opacity. E.g., if opacity is 1, the original
   * colorIntensity is returned; if opacity is 0, 1 is returned; if opacity is 0.5; colorIntensity/2 + 0.5 is
   * returned. It's odd, but when applied to an RGB value, it produces the desired effect.
   *
   * @class PDFCreator
   */
  applyOpacity(colorIntensity, opacity) {
    const backgroundColorIntensity = 1;
    return colorIntensity * opacity + backgroundColorIntensity * (1 - opacity);
  }
  /**
   * Draws a PNG image into the page.
   *
   * @params imageDataUrl Image to add to page
   * @params options Position information
   *
   * @class PDFCreator
   */
  drawImage(imageDataUrl, options) {
  }
  /**
   * @class PDFCreator
   */
  drawLine(options) {
    const updatedOptions = {
      ...this.lineOptions,
      ...options
    };
    updatedOptions.lineProperties = {
      ...this.lineOptions.lineProperties,
      ...options.lineProperties
    };
    this.updateLineProperties(updatedOptions.lineProperties);
    this.lineOptions = updatedOptions;
  }
  /**
   * @class PDFCreator
   */
  drawNeatline(lineProperties = {
    thickness: 5e-3,
    color: "0000ff",
    opacity: 0.2
  }) {
    this.drawRectangle({
      left: 0,
      top: 0,
      width: this.pageOptions.width - this.pageOptions.leftMargin - this.pageOptions.rightMargin,
      height: this.pageOptions.height - this.pageOptions.topMargin - this.pageOptions.bottomMargin,
      lineProperties
    });
  }
  /**
   * @class PDFCreator
   */
  drawRectangle(options) {
    const updatedOptions = {
      ...this.rectangleOptions,
      ...options
    };
    updatedOptions.lineProperties = {
      ...this.lineOptions.lineProperties,
      ...options.lineProperties
    };
    this.updateLineProperties(updatedOptions.lineProperties);
    if (this.rectangleOptions.fillColor !== updatedOptions.fillColor || this.rectangleOptions.fillOpacity !== updatedOptions.fillOpacity) {
      this.setFillColor(updatedOptions.fillColor, updatedOptions.fillOpacity);
    }
    this.rectangleOptions = updatedOptions;
  }
  /**
   * @class PDFCreator
   */
  drawTable(text, options) {
    const updatedOptions = {};
    updatedOptions.lineProperties = {
      ...this.tableOptions.lineProperties,
      ...options.lineProperties
    };
    updatedOptions.textProperties = {
      ...this.tableOptions.textProperties,
      ...options.textProperties
    };
    if (this.tableOptions.textProperties.fontPoints !== updatedOptions.textProperties.fontPoints) {
      this.setFontSize(updatedOptions.textProperties.fontPoints);
    }
    if (this.tableOptions.textProperties.fontColor !== updatedOptions.textProperties.fontColor) {
      this.setFontColor(updatedOptions.textProperties.fontColor);
    }
    this.updateLineProperties(updatedOptions.lineProperties);
    this.tableOptions = updatedOptions;
  }
  /**
   * @class PDFCreator
   */
  drawText(text, options) {
    const updatedOptions = {
      ...this.textOptions,
      ...options
    };
    if (this.textOptions.fontPoints !== updatedOptions.fontPoints) {
      this.setFontSize(updatedOptions.fontPoints);
    }
    if (this.textOptions.fontColor !== updatedOptions.fontColor) {
      this.setFontColor(updatedOptions.fontColor);
    }
    this.textOptions = updatedOptions;
  }
  /**
   * Calculates document units from top of font ascenders to baseline.
   *
   * @class PDFCreator
   */
  fontAscenderBaselineHeight(fontPoints) {
    return fontPoints * this.fontProps.fontResolutionInchesPerPoint;
  }
  /**
   * Calculates document units from top of font ascenders to bottom of descenders.
   *
   * @class PDFCreator
   */
  fontAscenderDescenderHeight(fontPoints) {
    return this.fontProps.fontFullHeightRatio * this.fontAscenderBaselineHeight(fontPoints);
  }
  /**
   * Returns the name of the font file supporting the specified locale.
   *
   * @param lang Locale such as "en" or "zh-tw"
   * @returns Font name, e.g., "0b7681dc140844ee9f409bdac249fbf0-japanese"
   *
   * @class PDFCreator
   */
  getFontFileName(lang) {
    let fontFile = "0b7681dc140844ee9f409bdac249fbf0-general";
    if (lang === "ja") {
      fontFile = "0b7681dc140844ee9f409bdac249fbf0-japanese";
    } else if (lang === "ko") {
      fontFile = "0b7681dc140844ee9f409bdac249fbf0-korean";
    } else if (lang === "zh-cn") {
      fontFile = "0b7681dc140844ee9f409bdac249fbf0-simplified-chinese";
    } else if (lang === "zh-hk" || lang === "zh-tw") {
      fontFile = "0b7681dc140844ee9f409bdac249fbf0-traditional-chinese";
    }
    return fontFile;
  }
  /**
   * Estimates with width of a string.
   *
   * @param text String to estimate
   * @param fontSize Text size in points
   * @returns Estimated width of text using (points x 1/72 inches/point) * 0.6;
   * need to override this generalized function with a specific PDF generator;
   * the 0.6 width:height ratio comes from https://www.fhwa.dot.gov/publications/research/safety/98057/ch03.cfm#width
   *
   * @class PDFCreator
   */
  getTextWidth(text, fontSize) {
    const ratioWidthToHeight = 0.6;
    return text.length * fontSize * this.fontProps.fontResolutionInchesPerPoint * ratioWidthToHeight;
  }
  /**
   * @class PDFCreator
   */
  hexToRGB(hex, opacity = 1) {
    const colorParts = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return colorParts ? {
      r: this.applyOpacity(parseInt(colorParts[1], 16) / 255, opacity),
      g: this.applyOpacity(parseInt(colorParts[2], 16) / 255, opacity),
      b: this.applyOpacity(parseInt(colorParts[3], 16) / 255, opacity)
    } : {
      r: 0,
      g: 0,
      b: 0
    };
  }
  /**
   * @class PDFCreator
   */
  async initialize(pageProperties = {}, dataPath = "", lang = "en", title = "", drawNeatline = false) {
    if (JSON.stringify(pageProperties) !== "{}") {
      this.pageOptions.pageType = pageProperties.pageType === EPageType.A4 ? EPageType.A4 : EPageType.ANSI_A;
      const pageSize = PDFCreator.getPageSize(this.pageOptions.pageType);
      this.pageOptions.width = pageSize.width;
      this.pageOptions.height = pageSize.height;
      this.pageOptions.leftMargin = pageProperties.leftMargin ?? this.pageOptions.leftMargin;
      this.pageOptions.rightMargin = pageProperties.rightMargin ?? this.pageOptions.rightMargin;
      this.pageOptions.topMargin = pageProperties.topMargin ?? this.pageOptions.topMargin;
      this.pageOptions.bottomMargin = pageProperties.bottomMargin ?? this.pageOptions.bottomMargin;
    }
    this.dataPath = dataPath;
    this.lang = lang;
    this.title = title || "PDF Document";
    this.pageOptions.drawNeatline = drawNeatline;
    return Promise.resolve();
  }
  /**
   * @class PDFCreator
   */
  save() {
  }
  /**
   * @class PDFCreator
   */
  setDrawColor(drawColor, opacity = 1) {
  }
  /**
   * @class PDFCreator
   */
  setFillColor(fillColor, opacity = 1) {
  }
  /**
   * @class PDFCreator
   */
  setFont(lang) {
    return Promise.resolve();
  }
  /**
   * @class PDFCreator
   */
  setFontColor(fontColor) {
  }
  /**
   * @class PDFCreator
   */
  setFontSize(fontPoints) {
  }
  /**
   * @class PDFCreator
   */
  setLang(lang) {
    if (this.lang !== lang) {
      this.lang = lang;
      return this.setFont(lang);
    } else {
      return Promise.resolve();
    }
  }
  /**
   * @class PDFCreator
   */
  setLineWidth(width) {
  }
  updateLineProperties(updatedProps) {
    if (this.lineOptions.lineProperties.thickness !== updatedProps.thickness) {
      this.setLineWidth(updatedProps.thickness);
    }
    if (this.lineOptions.lineProperties.color !== updatedProps.color || this.lineOptions.lineProperties.opacity !== updatedProps.opacity) {
      this.setDrawColor(updatedProps.color, updatedProps.opacity);
    }
    this.lineOptions.lineProperties = updatedProps;
  }
}
class PDFCreator_jsPDF extends PDFCreator {
  jsDoc;
  constructor(jspdfToUse = null) {
    super();
    this.jsDoc = jspdfToUse;
  }
  //-- Public methods ------------------------------------------------------------------------------------------------//
  /**
   * Adds a page to the document being created.
   *
   * @class PDFCreator_jsPDF
   */
  addPage() {
    super.addPage();
    this.jsDoc.addPage();
    if (this.pageOptions.drawNeatline) {
      this.drawNeatline();
    }
  }
  /**
   * Converts a locale into a jsPDF language code.
   *
   * @param lang Locale such as "en" or "en-gb"
   * @returns jsPDF_lang_code jsPDF language code such as "en" or "en-GB", mapping AGO locales "it-it" & "pt-pt"
   * into "it" & "pt", respectively, to match jsPDF offering
   *
   * @class PDFCreator_jsPDF
   */
  convertLocaleToJsPDFLanguageCode(lang) {
    let jsPDF_lang;
    if (lang === "it-it") {
      jsPDF_lang = "it";
    } else if (lang === "pt-pt") {
      jsPDF_lang = "pt";
    } else {
      const langParts = lang.split("-");
      if (langParts.length === 1) {
        jsPDF_lang = lang;
      } else {
        jsPDF_lang = langParts[0] + "-" + langParts[1].toUpperCase();
      }
    }
    return jsPDF_lang;
  }
  /**
   * Draws a PNG image into the page.
   *
   * @params imageDataUrl Image to add to page
   * @params options Position information
   *
   * @class PDFCreator
   */
  drawImage(imageDataUrl, options) {
    const alias = "";
    const compression = "NONE";
    const rotation = 0;
    this.jsDoc.addImage(imageDataUrl, "PNG", options.x, options.y, options.width, options.height, alias, compression, rotation);
  }
  /**
   * @class PDFCreator_jsPDF
   */
  drawLine(options) {
    super.drawLine(options);
    this.jsDoc.line(
      this.pageOptions.leftMargin + this.lineOptions.x1,
      this.pageOptions.topMargin + this.lineOptions.y1,
      this.pageOptions.leftMargin + this.lineOptions.x2,
      this.pageOptions.topMargin + this.lineOptions.y2,
      this.lineOptions.lineProperties.color ? "S" : ""
      // line vs. nothing
    );
  }
  /**
   * @class PDFCreator_jsPDF
   */
  drawRectangle(options) {
    super.drawRectangle(options);
    this.jsDoc.rect(
      this.pageOptions.leftMargin + this.rectangleOptions.left,
      this.pageOptions.topMargin + this.rectangleOptions.top,
      this.rectangleOptions.width,
      this.rectangleOptions.height,
      this.rectangleOptions.lineProperties.color ? this.rectangleOptions.fillColor ? "FD" : "S" : this.rectangleOptions.fillColor ? "F" : ""
      // fill only vs. nothing
    );
  }
  /**
   * @class PDFCreator_jsPDF
   */
  drawTable(text, options) {
    super.drawTable(text, options);
    const left = this.pageOptions.leftMargin + this.textOptions.left;
    const top = this.pageOptions.topMargin + this.textOptions.top;
    this.jsDoc.text(text[0][0], left, top);
  }
  /**
   * @class PDFCreator_jsPDF
   */
  drawText(text, options) {
    super.drawText(text, options);
    let left = this.pageOptions.leftMargin + this.textOptions.left;
    if (this.lang === "ar" || this.lang === "he") {
      const textWidth = this.jsDoc.context2d.measureText(text).width * this.fontProps.fontResolutionInchesPerPoint;
      left = this.pageOptions.width - this.pageOptions.leftMargin - textWidth - this.textOptions.left;
    }
    const top = this.pageOptions.topMargin + this.textOptions.top + this.fontAscenderBaselineHeight(this.textOptions.fontPoints);
    this.jsDoc.text(text, left, top);
  }
  /**
   * Estimates with width of a string.
   *
   * @param {string} text String to estimate
   * @param fontSize Text size in points
   * @returns {number} Estimated width of text in doc units
   *
   * @class PDFCreator_jsPDF
   */
  getTextWidth(text, fontSize) {
    return this.jsDoc.getStringUnitWidth(text) * fontSize / this.jsDoc.internal.scaleFactor;
  }
  /**
   * @class PDFCreator_jsPDF
   */
  async initialize(pageProperties = {}, dataPath = "", lang = "en", title = "", drawNeatline = false) {
    await super.initialize(pageProperties, dataPath, lang, title, drawNeatline);
    if (!this.jsDoc) {
      this.jsDoc = new jsPDF({
        format: pageProperties.pageType === EPageType.A4 ? "a4" : "letter",
        orientation: "portrait",
        putOnlyUsedFonts: true,
        unit: "in"
      });
    }
    this.jsDoc.setDocumentProperties({
      title: this.title,
      keywords: "Created with ArcGIS Online"
    });
    if (this.pageOptions.drawNeatline) {
      this.drawNeatline();
    }
    await this.setFont(lang);
  }
  /**
   * @class PDFCreator_jsPDF
   */
  save() {
    this.jsDoc.save(this.title + ".pdf");
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setDrawColor(drawColor, opacity = 1) {
    const rgb = this.hexToRGB(drawColor, opacity);
    this.jsDoc.setDrawColor(rgb.r.toString(), rgb.g.toString(), rgb.b.toString());
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setFillColor(fillColor, opacity = 1) {
    const rgb = this.hexToRGB(fillColor, opacity);
    this.jsDoc.setFillColor(rgb.r.toString(), rgb.g.toString(), rgb.b.toString());
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setFont(lang) {
    this.jsDoc.setLanguage(this.convertLocaleToJsPDFLanguageCode(lang));
    const fontFileName = this.getFontFileName(lang);
    if (this.jsDoc.existsFileInVFS(fontFileName + ".ttf")) {
      this.jsDoc.setFont(fontFileName, "normal", "normal");
      return Promise.resolve();
    }
    return fetch(this.dataPath + fontFileName + ".txt").then((fontFile) => {
      return fontFile.text();
    }).then((font) => {
      this.jsDoc.addFileToVFS(fontFileName + ".ttf", font);
      this.jsDoc.addFont(fontFileName + ".ttf", fontFileName, "normal", "normal");
      this.jsDoc.setFont(fontFileName, "normal", "normal");
      return Promise.resolve();
    });
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setFontColor(fontColor) {
    const rgb = this.hexToRGB(fontColor);
    this.jsDoc.setTextColor(rgb.r.toString(), rgb.g.toString(), rgb.b.toString());
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setFontSize(fontPoints) {
    this.jsDoc.setFontSize(fontPoints);
  }
  /**
   * @class PDFCreator_jsPDF
   */
  setLineWidth(width) {
    this.jsDoc.setLineWidth(width);
  }
}
function drawGridOfBoxes(PDFCreator2, options = {
  numAcross: 1,
  numDown: 1,
  x0: 0,
  y0: 0,
  width: 1,
  height: 1,
  horizGap: 0,
  vertGap: 0,
  lineProperties: {
    thickness: 0,
    color: "",
    opacity: 1
    // 0..1
  }
}) {
  for (let j = 0; j < options.numDown; ++j) {
    for (let i = 0; i < options.numAcross; ++i) {
      PDFCreator2.drawRectangle({
        left: options.x0 + i * (options.width + options.horizGap),
        top: options.y0 + j * (options.height + options.vertGap),
        width: options.width,
        height: options.height,
        lineProperties: options.lineProperties
      });
    }
  }
}
class PDFLabels {
  // Properties are public for testing purposes
  PDFCreator;
  labelFormats;
  //-- Public methods ------------------------------------------------------------------------------------------------//
  /**
   * Add labels to a PDF doc, starting at the beginning of the specified PDF doc page.
   *
   * @param labels Array of labels; each label consists of one or more line strings
   * @param labelSpec Properties describing page dimensions of labels sheet
   * @param startingPageNum The 1-based page number to start printing labels into; page is assumed to be blank & to exist
   * @param heading Line to add at top of label page outside of labels
   * @param progressCallback Callback with percent done in range 0..100
   * @returns Promise which, when resolved, returns the 1-based page number of the last page containing labels
   *
   * @class PDFLabels
   */
  addLabelsToDoc(labels, labelSpec, startingPageNum, heading, progressCallback) {
    return new Promise((resolve) => {
      const labelsPerPageDisplay = labelSpec.numLabelsAcross * labelSpec.numLabelsDown;
      let column;
      let row;
      let currentLabelLeft;
      let currentLabelTop;
      const maxLabelTextWidth = labelSpec.labelWidth - 2 * labelSpec.labelPadding;
      let currentPageNum = startingPageNum;
      const topOfFirstLabel = labelSpec.pageProperties.topMargin - this.PDFCreator.pageOptions.topMargin;
      if (heading) {
        this.drawSupplementalText(heading, 0, -0.1);
      }
      for (let iLabel = 0, iNonBlankLabel = 0; iLabel < labels.length; iLabel++) {
        if (progressCallback) {
          progressCallback(Math.round(iLabel / labels.length * 100));
        }
        let labelLines = labels[iLabel].map((line) => line.trim()).filter((line) => line.length > 0);
        if (labelLines.length === 0) {
          continue;
        }
        if (iNonBlankLabel % labelsPerPageDisplay === 0) {
          if (iNonBlankLabel > 0) {
            this.PDFCreator.addPage();
            ++currentPageNum;
            if (heading) {
              this.drawSupplementalText(heading, 0, -0.1);
            }
          }
          column = 0;
          currentLabelLeft = 0;
          row = 0;
        } else if (iNonBlankLabel % labelSpec.numLabelsDown === 0) {
          currentLabelLeft = ++column * (labelSpec.labelWidth + labelSpec.horizGapIn);
          row = 0;
        }
        const minimumFontSize = 4;
        let fontPoints = labelSpec.fontSizePx;
        if (labelSpec.maxNumLabelLines < labelLines.length) {
          fontPoints = Math.max(Math.floor(labelSpec.maxNumLabelLines / labelLines.length * labelSpec.fontSizePx), minimumFontSize);
        }
        fontPoints = this._getFontSizeForOverlongLines(labelLines, maxLabelTextWidth, fontPoints, minimumFontSize);
        labelLines = this._clipOverlongLines(labelLines, maxLabelTextWidth, fontPoints);
        const labelLineHeight = this.PDFCreator.fontAscenderDescenderHeight(fontPoints);
        currentLabelTop = topOfFirstLabel + row++ * (labelSpec.labelHeight + labelSpec.vertGapIn);
        labelLines.forEach((line) => {
          this.PDFCreator.drawText(line, {
            left: currentLabelLeft + labelSpec.labelPadding,
            top: currentLabelTop + labelSpec.labelPadding,
            fontPoints,
            fontColor: "000000"
          });
          currentLabelTop += labelLineHeight;
        });
        ++iNonBlankLabel;
      }
      if (progressCallback) {
        progressCallback(100);
      }
      resolve(currentPageNum);
    });
  }
  drawLabelGuidelines(labelSpec, labelBoundaryLinesProperties = null) {
    if (labelBoundaryLinesProperties !== null) {
      drawGridOfBoxes(this.PDFCreator, {
        // Margins in the page spec are absolute but we draw in within the margins
        x0: labelSpec.pageProperties.leftMargin - this.PDFCreator.pageOptions.leftMargin,
        y0: labelSpec.pageProperties.topMargin - this.PDFCreator.pageOptions.topMargin,
        numAcross: labelSpec.numLabelsAcross,
        numDown: labelSpec.numLabelsDown,
        width: labelSpec.labelWidth,
        height: labelSpec.labelHeight,
        horizGap: labelSpec.horizGapIn,
        vertGap: labelSpec.vertGapIn,
        lineProperties: labelBoundaryLinesProperties
      });
    }
  }
  /**
   * Draws supplemental text such as a heading or footer.
   *
   * @param text Text to draw
   * @param left Offset from left edge of document to left edge of text
   * @param top Offset from top of document to top of text
   */
  drawSupplementalText(text, left, top) {
    const fontPoints = 6;
    this.PDFCreator.drawText(text, {
      left,
      top,
      fontPoints,
      fontColor: "000000"
    });
  }
  /**
   * Returns the UI descriptions of the label formats.
   *
   * @returns List of label format descriptions
   *
   * @class PDFLabels
   */
  getAvailableLabelFormats() {
    if (this.labelFormats.length > 0) {
      return this.labelFormats.map((format) => format.descriptionPDF);
    } else {
      return [];
    }
  }
  /**
   * Returns the format of the specified label.
   *
   * @param averyPartNumber Avery® label base part number, e.g., "*60" (versus actual part numbers "5160", "8160",
   * "8460", "48160"--all of which are 1" x 2-5/8" rectangular labels)
   * @returns List of label format descriptions
   *
   * @class PDFLabels
   */
  getLabelFormat(averyPartNumber) {
    if (this.labelFormats.length > 0) {
      const matches = this.labelFormats.filter((format) => averyPartNumber === format.descriptionPDF.averyPartNumber);
      if (matches.length > 0) {
        return matches[0];
      } else {
        return null;
      }
    } else {
      return null;
    }
  }
  /**
   * Loads language font file and label formats into a jsPDF `doc` object.
   *
   * @param PDFCreator PDF-drawing library
   * @throws TypeError "startPDFLabelDoc is stomping on labelFormats property" if the `labelFormats` property
   * is found in doc before the label formats are loaded into the doc.
   *
   * @class PDFLabels
   */
  initialize(PDFCreator2) {
    this.PDFCreator = PDFCreator2;
    return this._loadLabelFormats(PDFCreator2.dataPath).then((labelFormats) => {
      this.labelFormats = labelFormats;
      return Promise.resolve();
    });
  }
  /**
   * Removes duplicates and empties, trims label lines, and returns a sorted list.
   *
   * @class PDFLabels
   */
  removeDuplicates(labels) {
    const nonEmptyLabels = labels.filter((label) => Array.isArray(label) && label.length > 0);
    const compressedLabels = nonEmptyLabels.map((label) => label.map((labelLine) => labelLine.trim()).join("?"));
    const uniqueLabels = {};
    compressedLabels.forEach((comprLabel) => uniqueLabels[comprLabel] = true);
    const screenedLabels = Object.keys(uniqueLabels).sort().map((label) => label.split("?"));
    return screenedLabels;
  }
  //-- Private methods -----------------------------------------------------------------------------------------------//
  /**
   * Calculates the baseline position of the first line of text in a label.
   *
   * @param labelTop Offset from top of document to top of label
   * @param labelHeight Height of each label in doc units
   * @param numLabelLines Number of lines to be drawn in the label
   * @param fontHeight Height of text in doc units
   * @param verticalFontGap Spacing between lines of text in doc units
   * @returns Baseline position
   *
   * @class PDFLabels
   * @private
   */
  _calculateLabelFirstLineBase(labelTop, labelHeight, numLabelLines, fontHeight, verticalFontGap) {
    const labelVerticalMidpoint = labelTop + labelHeight / 2;
    return labelVerticalMidpoint + (2 - numLabelLines) / 2 * fontHeight + (1 - numLabelLines) / 2 * verticalFontGap;
  }
  /**
   * Trims a set of text lines to fit within specified bounds.
   *
   * @param lines Text lines to be trimmed
   * @param maxTextWidth The maximum width permitted for the drawing of the text in doc units
   * @param fontSize Text size in points
   * @returns A copy of lines trimmed as necessary
   *
   * @class PDFLabels
   * @private
   */
  _clipOverlongLines(lines, maxTextWidth, fontSize) {
    const trimmedLines = [];
    lines.forEach((line) => {
      line = line.trim();
      if (this.PDFCreator.getTextWidth(line, fontSize) > maxTextWidth) {
        do {
          line = line.slice(0, -1);
        } while (this.PDFCreator.getTextWidth(line, fontSize) > maxTextWidth);
        line += "...";
      }
      trimmedLines.push(line);
    });
    return trimmedLines;
  }
  /**
   * Trims a set of text lines to fit within specified bounds.
   *
   * @param lines Text lines to be checked
   * @param maxTextWidth The maximum width permitted for the drawing of the text in doc units
   * @param preferredFontSize Initial text size in points
   * @param minimumFontSize The smallest acceptable font size in points
   * @returns The maximum of `minimumFontSize` and the font size in points that should help all lines
   * of the label to fit on the label
   *
   * @class PDFLabels
   * @private
   */
  _getFontSizeForOverlongLines(lines, maxTextWidth, preferredFontSize, minimumFontSize) {
    let fontSize = preferredFontSize;
    lines.forEach((line) => {
      line = line.trim();
      if (this.PDFCreator.getTextWidth(line, fontSize) > maxTextWidth) {
        do {
          --fontSize;
        } while (this.PDFCreator.getTextWidth(line, fontSize) > maxTextWidth && fontSize > minimumFontSize);
      }
    });
    return fontSize;
  }
  /**
   * Loads the label formats.
   *
   * @param dataPath Path to the library's data files
   * @returns Promise resolving to the label formats JSON
   *
   * @class PDFLabels
   * @private
   */
  async _loadLabelFormats(dataPath) {
    const labelFormatsFile = await fetch(dataPath + "labelFormats.json");
    const labelFormatsJson = await labelFormatsFile.json();
    return labelFormatsJson;
  }
}
function exportPDF(filename, labels, labelPageDescription, title = "", initialImageDataUrl = "") {
  void downloadPDFFile(filename, labels, labelPageDescription, title, initialImageDataUrl);
}
async function downloadPDFFile(filename, labels, labelPageDescription, title = "", initialImageDataUrl = "") {
  const pdfLib = new PDFCreator_jsPDF(void 0);
  await pdfLib.initialize(
    {
      pageType: "ANSI_A"
    },
    getAssetPath(`assets/arcgis-pdf-creator/`),
    "en",
    filename
    // filename without ".pdf"
  );
  const labeller = new PDFLabels();
  await labeller.initialize(pdfLib);
  const labelSpec = labelPageDescription.labelSpec;
  let startingPageNum = 1;
  if (initialImageDataUrl) {
    const pageProperties = labelSpec.pageProperties;
    const pageSize = PDFCreator.getPageSize(pageProperties.pageType || "");
    pdfLib.drawImage(initialImageDataUrl, {
      x: pageProperties.leftMargin || 0,
      y: pageProperties.topMargin || 0,
      width: pageSize.width - (pageProperties.leftMargin || 0) - (pageProperties.rightMargin || 0),
      height: pageSize.height - (pageProperties.topMargin || 0) - (pageProperties.bottomMargin || 0)
    });
    if (title) {
      labeller.drawSupplementalText(title, 0, -0.1);
    }
    if (labels.length > 0) {
      pdfLib.addPage();
      ++startingPageNum;
    }
  }
  await labeller.addLabelsToDoc(
    labels,
    labelSpec,
    startingPageNum,
    title
    // heading
  );
  pdfLib.save();
}
const lineSeparatorChar = "|";
async function consolidateLabels(webmap, exportInfos, formatUsingLayerPopup = true, includeHeaderNames = false, isCSVExport = false, fields = void 0, useFieldAliasNames = false, filterFields = false) {
  const labelRequests = [];
  Object.keys(exportInfos).forEach((k) => {
    const labelInfo = exportInfos[k];
    const layer = labelInfo.layerView?.layer || labelInfo.layer;
    if (layer) {
      labelRequests.push(
        _prepareLabels(webmap, layer, labelInfo.ids, formatUsingLayerPopup, includeHeaderNames, fields, useFieldAliasNames, filterFields)
      );
    }
    if (isCSVExport) {
      labelRequests.push(Promise.resolve([[k]]));
    }
  });
  const labels = await Promise.all(labelRequests);
  return labels.reduce((prev, cur) => prev.concat(cur), []);
}
async function downloadCSV(webmap, exportInfos, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false, fields = void 0, useFieldAliasNames = false, filterFields = false) {
  let labels = await consolidateLabels(webmap, exportInfos, formatUsingLayerPopup, addColumnTitle, true, fields, useFieldAliasNames, filterFields);
  labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
  const layerIds = Object.keys(exportInfos);
  let layerLabels = [];
  labels.forEach((label) => {
    const id = label[0];
    if (layerIds.indexOf(id) < 0) {
      layerLabels.push(label);
    } else {
      const selectionSetNames = _getSelectionSetNames(exportInfos, new RegExp(`\\b${id}\\b`));
      exportCSV(_createFilename(selectionSetNames), layerLabels);
      layerLabels = [];
    }
  });
  return Promise.resolve();
}
async function downloadPDF(webmap, exportInfos, labelPageDescription, removeDuplicates = false, title = "", initialImageDataUrl = "") {
  let labels = await consolidateLabels(webmap, exportInfos);
  const selectionSetNames = _getSelectionSetNames(exportInfos);
  labels = // Remove empty lines in labels
  labels.map((labelLines) => labelLines.filter((line) => line.length > 0)).filter((label) => label.length > 0);
  labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
  const exportTitle = title ? title : _createFilename(selectionSetNames);
  exportPDF(exportTitle, labels, labelPageDescription, title, initialImageDataUrl);
  return Promise.resolve();
}
function removeDuplicateLabels(labels) {
  const labelsAsStrings = labels.map((label) => JSON.stringify(label));
  const uniqueLabels = new Set(labelsAsStrings);
  return Array.from(uniqueLabels, (labelString) => JSON.parse(labelString));
}
function _cleanupLabel(labelText) {
  labelText = labelText.replace(/<br\s*\/?>/gi, lineSeparatorChar);
  labelText = labelText.replace(/<p[^>]*>/gi, lineSeparatorChar).trim().replace(/^\|/, "");
  labelText = labelText.replace(/<\/p>/gi, "");
  labelText = labelText.replace(/\n/gi, "|");
  const sanitizer = new Sanitizer();
  labelText = sanitizer.sanitize(labelText);
  labelText = labelText.replace(/<[\s.]*[^<>]*\/?>/gi, "").replace(/\xA0/gi, " ").replace(/&nbsp;/gi, " ");
  labelText = labelText.replace(/\s*\|\s*/g, "|");
  while (labelText.match(/\|\|/)) {
    labelText = labelText.replace(/\|\|/, "|");
  }
  labelText = labelText.replace(/^\|/, "");
  labelText = labelText.replace(/\|$/, "");
  return labelText;
}
function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false) {
  const labelSpec = [];
  fieldInfos.forEach((fieldInfo) => {
    if (fieldInfo.visible || bypassFieldVisiblity) {
      labelSpec.push(`{${fieldInfo.fieldName}}`);
    }
  });
  return {
    type: "pattern",
    format: labelSpec.join(lineSeparatorChar)
  };
}
function _convertPopupTextToLabelSpec(popupInfo) {
  return {
    type: "pattern",
    format: _cleanupLabel(popupInfo)
  };
}
async function _convertPopupArcadeToLabelSpec(expressionInfo) {
  const labelingProfile = {
    variables: [
      {
        name: "$feature",
        type: "feature"
      },
      {
        name: "$layer",
        type: "featureSet"
      },
      {
        name: "$datastore",
        type: "featureSetCollection"
      },
      {
        name: "$map",
        type: "featureSetCollection"
      }
    ]
  };
  const executor = await arcade.createArcadeExecutor(expressionInfo.expression, labelingProfile);
  return Promise.resolve({
    type: "executor",
    format: executor
  });
}
async function _createArcadeExecutors(labelFormat, layer) {
  const arcadeExecutors = {};
  if (!Array.isArray(layer?.popupTemplate?.expressionInfos) || layer?.popupTemplate?.expressionInfos.length === 0) {
    return Promise.resolve(arcadeExecutors);
  }
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
  const arcadeExpressionsMatches = labelFormat.match(arcadeExpressionRegExp);
  if (!arcadeExpressionsMatches) {
    return Promise.resolve(arcadeExecutors);
  }
  const labelingProfile = {
    variables: [
      {
        name: "$feature",
        type: "feature"
      },
      {
        name: "$layer",
        type: "featureSet"
      },
      {
        name: "$datastore",
        type: "featureSetCollection"
      },
      {
        name: "$map",
        type: "featureSetCollection"
      }
    ]
  };
  const createArcadeExecutorPromises = {};
  arcadeExpressionsMatches.forEach((match) => {
    const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
    (layer?.popupTemplate?.expressionInfos || []).forEach((expressionInfo) => {
      if (expressionInfo.name === expressionName) {
        createArcadeExecutorPromises[expressionName] = arcade.createArcadeExecutor(expressionInfo.expression, labelingProfile);
      }
    });
  });
  const promises = Object.values(createArcadeExecutorPromises);
  return Promise.all(promises).then((executors) => {
    const expressionNames = Object.keys(createArcadeExecutorPromises);
    for (let i = 0; i < expressionNames.length; ++i) {
      arcadeExecutors[expressionNames[i]] = executors[i].valueOf();
    }
    return arcadeExecutors;
  });
}
function _createFilename(selectionSetNames) {
  const title = selectionSetNames.length > 0 ? selectionSetNames.join(", ") : "download";
  return title;
}
function _extractHeaderNames(labelFormat) {
  const lines = labelFormat.split(lineSeparatorChar);
  const headerNames = lines.map((line, i) => {
    const fieldNameSyms = line.match(/\{[^\}]*\}/g);
    return fieldNameSyms ? fieldNameSyms.map((fieldName) => fieldName.substring(1, fieldName.length - 1)).map((fieldName) => fieldName.startsWith("expression/") ? fieldName.substring(fieldName.indexOf("/") + 1) : fieldName).join("__") : `column_${i + 1}`;
  });
  return headerNames;
}
function _getExpressionsFromLabel(labelFormat) {
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
  return labelFormat.match(arcadeExpressionRegExp) ?? [];
}
function _getFeatureServiceRelatedRecords(url, layerId, relationshipId, objectIds, token) {
  const endOfUrl = url.substring(url.lastIndexOf("/") + 1);
  if (Number.isNaN(Number.parseInt(endOfUrl))) {
    url += "/" + layerId.toString();
  }
  const options = {
    definitionExpression: "1=1",
    httpMethod: "POST",
    objectIds,
    outFields: "*",
    relationshipId,
    url
  };
  if (token) {
    options.params = { token };
  }
  return _getFeatureServiceRelatedRecordsTranche(options);
}
function _getFeatureServiceRelatedRecordsTranche(options, relationships = [], resultOffset = 0) {
  const params = {
    ...options.params,
    resultOffset
  };
  return common.queryRelated({
    ...options,
    params
    //TODO } as IQueryRelatedOptionsOffset).then((response: IQueryRelatedResponseOffset) => {
  }).then((response) => {
    relationships.push(...response.relatedRecordGroups);
    return response.exceededTransferLimit ? _getFeatureServiceRelatedRecordsTranche(options, relationships, resultOffset + response.relatedRecordGroups.length) : Promise.resolve(relationships);
  });
}
function _getFieldExpressionsFromLabel(labelFormat) {
  const fieldExpressions = [];
  let iStart = 0;
  while (iStart < labelFormat.length) {
    const iOpen = labelFormat.indexOf("{", iStart);
    if (iOpen < 0) {
      break;
    }
    const iClose = labelFormat.indexOf("}", iOpen);
    if (iClose < 0) {
      break;
    }
    const fieldName = labelFormat.substring(iOpen, iClose + 1);
    fieldExpressions.push(fieldName);
    iStart = iClose + 1;
  }
  const arcadeExpressions = _getExpressionsFromLabel(labelFormat);
  return fieldExpressions.filter((fieldExpression) => arcadeExpressions.indexOf(fieldExpression) < 0);
}
function _getFieldNamesFromFieldExpressions(fieldExpressions) {
  return fieldExpressions.map((expr) => expr.substring(1, expr.length - 1));
}
async function _getLabelFormat(webmap, layer, formatUsingLayerPopup) {
  let labelFormatProps = {
    layer,
    attributeFormats: {},
    relationshipId: void 0,
    labelFormat: {
      type: "unsupported",
      format: void 0
    }
  };
  if (layer.popupEnabled) {
    (layer?.popupTemplate?.fieldInfos || []).forEach(
      // Extract any format info that we have
      (fieldInfo) => {
        if (fieldInfo.format) {
          labelFormatProps.attributeFormats[fieldInfo.fieldName.toLowerCase()] = fieldInfo.format;
        }
      }
    );
    if (formatUsingLayerPopup && layer) {
      const content = layer?.popupTemplate?.content || [];
      const labelFormatType = content && content.length > 0 ? content[0]?.type : void 0;
      if (labelFormatType === "relationship") {
        const relationshipId = content[0].relationshipId;
        const layerRelationship = layer.relationships?.find((relationship) => relationship.id === relationshipId);
        let webmapLayers = webmap.layers.toArray().concat(webmap.tables.toArray());
        webmapLayers = webmapLayers.filter((webmapLayer) => webmapLayer.type === "feature" && webmapLayer.layerId === layerRelationship?.relatedTableId);
        if (webmapLayers.length > 0) {
          labelFormatProps = await _getLabelFormat(webmap, webmapLayers[0], formatUsingLayerPopup);
          labelFormatProps.relationshipId = relationshipId.toString();
        } else {
          webmapLayers = webmap.layers.toArray().concat(webmap.tables.toArray());
          const relationshipCandidatePromises = [];
          webmapLayers = webmapLayers.filter((webmapLayer) => {
            if (webmapLayer.type === "feature" && webmapLayer.layerId != layer.layerId && webmapLayer.url) {
              const webmapLayerUrl = webmapLayer.url + "/" + webmapLayer?.layerId?.toString();
              relationshipCandidatePromises.push(common.getJson(
                webmapLayerUrl
                /*, authentication*/
              ));
              return true;
            } else {
              return false;
            }
          });
          const relationshipCandidates = await Promise.all(relationshipCandidatePromises);
          let labelFormatPropsPromise = Promise.resolve(void 0);
          if (!relationshipCandidates.some((candidateLayer, i) => {
            if (candidateLayer.relationships && candidateLayer.relationships.some((relationship) => relationship.id === relationshipId)) {
              labelFormatPropsPromise = _getLabelFormat(webmap, webmapLayers[i], formatUsingLayerPopup);
              return true;
            } else {
              return false;
            }
          })) {
            labelFormatProps.labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate?.fieldInfos);
          } else {
            labelFormatProps = await labelFormatPropsPromise;
            labelFormatProps.relationshipId = relationshipId.toString();
          }
        }
      } else if (labelFormatType === "fields") {
        labelFormatProps.labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate?.fieldInfos);
        if (labelFormatProps.labelFormat.format.length === 0) {
          labelFormatProps.labelFormat = layer.popupTemplate?.title && typeof layer.popupTemplate?.title === "string" ? { type: "pattern", format: layer.popupTemplate.title } : (
            // Otherwise revert to using attributes
            _convertPopupFieldsToLabelSpec(layer.popupTemplate?.fieldInfos, true)
          );
        }
      } else if (labelFormatType === "text") {
        labelFormatProps.labelFormat = _convertPopupTextToLabelSpec(content[0].text);
      } else if (labelFormatType === "expression") {
        labelFormatProps.labelFormat = await _convertPopupArcadeToLabelSpec(content[0].expressionInfo);
      } else {
        labelFormatProps.labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate?.fieldInfos);
      }
    }
  }
  return Promise.resolve(labelFormatProps);
}
function _getSelectionSetNames(exportInfos, id = /.+/) {
  let selectionSetNames = [];
  Object.keys(exportInfos).forEach((k) => {
    const exportInfo = exportInfos[k];
    if (id.test(k)) {
      selectionSetNames = selectionSetNames.concat(exportInfo.selectionSetNames);
    }
  });
  return selectionSetNames;
}
function _prepareAttributeValue(attributeValue, attributeType, attributeDomain, attributeFormat, intl2) {
  if (attributeValue === null || typeof attributeValue === "undefined") {
    return "";
  }
  if (attributeDomain && attributeDomain.type === "coded-value") {
    const value = attributeDomain.getName(attributeValue);
    return value?.toString();
  } else {
    let value = attributeValue;
    switch (attributeType) {
      case "date":
        if (attributeFormat?.dateFormat) {
          const dateFormatIntlOptions = intl2.convertDateFormatToIntlOptions(attributeFormat.dateFormat);
          value = intl2.formatDate(value, dateFormatIntlOptions);
        } else {
          value = intl2.formatDate(value);
        }
        value = value.replace(/\xe2\x80\xaf/g, "");
        break;
      case "date-only":
        if (attributeFormat?.dateFormat) {
          const dateFormatIntlOptions = intl2.convertDateFormatToIntlOptions(attributeFormat.dateFormat);
          value = intl2.formatDateOnly(value, dateFormatIntlOptions);
        } else {
          value = intl2.formatDateOnly(value);
        }
        break;
      case "time-only":
        if (attributeFormat?.dateFormat) {
          const dateFormatIntlOptions = intl2.convertDateFormatToIntlOptions(attributeFormat.dateFormat);
          value = intl2.formatTimeOnly(value, dateFormatIntlOptions);
        } else {
          value = intl2.formatTimeOnly(value);
        }
        break;
      case "double":
      case "integer":
      case "long":
      case "small-integer":
        if (attributeFormat) {
          const numberFormatIntlOptions = intl2.convertNumberFormatToIntlOptions(attributeFormat);
          value = intl2.formatNumber(value, numberFormatIntlOptions);
        } else {
          value = intl2.formatNumber(value?.toString(), {
            maximumFractionDigits: 100
          });
        }
        break;
    }
    return value;
  }
}
async function _prepareLabels(webmap, layer, ids, formatUsingLayerPopup = true, includeHeaderNames = false, fields = [], useFieldAliasNames = false, filterFields = false) {
  const token = webmap?.portalItem?.portal?.credential?.token;
  const labelFormatProps = await _getLabelFormat(webmap, layer, formatUsingLayerPopup);
  const featureLayer = labelFormatProps.layer;
  let featureSet = [];
  if (typeof labelFormatProps.relationshipId !== "undefined") {
    const relatedRecordGroups = await _getFeatureServiceRelatedRecords(layer.url, layer.layerId, labelFormatProps.relationshipId, ids, token);
    const relatedFeatures = [];
    relatedRecordGroups.forEach((relatedRecGroup) => {
      relatedRecGroup.relatedRecords?.forEach((relatedRec) => {
        relatedFeatures.push(relatedRec.attributes);
      });
    });
    const objectIdField = layer.objectIdField;
    relatedFeatures.sort((a, b) => {
      if (a[objectIdField] < b[objectIdField]) {
        return -1;
      } else if (a[objectIdField] > b[objectIdField]) {
        return 1;
      } else {
        return 0;
      }
    });
    featureSet = relatedFeatures.filter((feature, i) => i === 0 ? true : feature[objectIdField] !== relatedFeatures[i - 1][objectIdField]);
    if (featureSet.length === 0) {
      return Promise.resolve([]);
    }
  } else {
    const outFields = fields.length > 0 ? fields : void 0;
    featureSet = await queryFeaturesByID(ids, featureLayer, [], false, void 0, outFields);
  }
  const attributeOrigNames = {};
  const attributeTypes = {};
  const attributeDomains = {};
  if (featureLayer.fields && fields) {
    featureLayer.fields.forEach((field) => {
      if (filterFields ? fields.indexOf((field?.name || "").toLowerCase()) > -1 : true) {
        const lowercaseFieldname = field.name.toLowerCase();
        attributeOrigNames[lowercaseFieldname] = field.name;
        attributeDomains[lowercaseFieldname] = field.domain;
        attributeTypes[lowercaseFieldname] = field.type;
      }
    });
  } else if (fields) {
    Object.keys(featureSet[0]).forEach((fieldName) => {
      if (filterFields ? fields.indexOf(fieldName.toLowerCase()) > -1 : true) {
        const lowercaseFieldname = fieldName.toLowerCase();
        attributeOrigNames[lowercaseFieldname] = fieldName;
      }
    });
  }
  const labels = labelFormatProps.labelFormat.type === "pattern" ? (
    // Export attributes in format
    await _prepareLabelsFromPattern(
      layer,
      featureSet,
      attributeOrigNames,
      attributeTypes,
      attributeDomains,
      labelFormatProps.attributeFormats,
      labelFormatProps.labelFormat.format,
      includeHeaderNames
    )
  ) : labelFormatProps.labelFormat.type === "executor" ? (
    // Export attributes in expression
    await _prepareLabelsUsingExecutor(featureSet, labelFormatProps.labelFormat.format)
  ) : (
    // Export all attributes
    await _prepareLabelsFromAll(featureSet, attributeTypes, attributeDomains, includeHeaderNames, useFieldAliasNames)
  );
  return Promise.resolve(labels);
}
async function _prepareLabelsFromAll(featureSet, attributeTypes, attributeDomains, includeHeaderNames = false, useFieldAliasNames = false) {
  const labels = featureSet.map((feature) => {
    return Object.keys(feature.attributes).map((attributeName) => {
      const lowercaseFieldname = attributeName.toLowerCase();
      return _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[lowercaseFieldname], attributeDomains[lowercaseFieldname], void 0, intl);
    });
  });
  if (includeHeaderNames) {
    const headerNames = [];
    const featuresAttrs = featureSet[0].attributes;
    const fieldHash = featureSet[0]?.layer?.fields.reduce((prev, cur) => {
      prev[cur.name] = cur.alias;
      return prev;
    }, {});
    Object.keys(featuresAttrs).forEach((k) => {
      headerNames.push(useFieldAliasNames && fieldHash ? fieldHash[k] : k);
    });
    labels.unshift(headerNames);
  }
  return Promise.resolve(labels);
}
async function _prepareLabelsFromPattern(layer, featureSet, attributeOrigNames, attributeTypes, attributeDomains, attributeFormats, labelFormat, includeHeaderNames = false) {
  const attributeExpressionMatches = _getFieldExpressionsFromLabel(labelFormat);
  const attributeNames = _getFieldNamesFromFieldExpressions(attributeExpressionMatches);
  const arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
  const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp) ?? [];
  const labels = await Promise.all(
    featureSet.map(async (feature) => {
      let labelPrep = labelFormat;
      for (let i = 0; i < arcadeExpressionMatches.length; i++) {
        const match = arcadeExpressionMatches[i];
        const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
        const value = await arcadeExecutors[expressionName].executeAsync({ $feature: feature, $layer: layer });
        if (value) {
          labelPrep = labelPrep.replace(match, value).replace(/\n/gi, "|");
        }
      }
      const attributeValues = feature.attributes ?? feature;
      attributeNames.forEach((attributeName, i) => {
        const lowercaseFieldname = attributeName.toLowerCase();
        const value = _prepareAttributeValue(
          attributeValues[attributeOrigNames[lowercaseFieldname]],
          attributeTypes[lowercaseFieldname],
          attributeDomains[lowercaseFieldname],
          attributeFormats[lowercaseFieldname],
          intl
        );
        labelPrep = labelPrep.replace(attributeExpressionMatches[i], value);
      });
      let label = labelPrep.split(lineSeparatorChar);
      label = label.map((line) => _cleanupLabel(line));
      return label;
    })
  );
  if (includeHeaderNames) {
    labels.unshift(_extractHeaderNames(labelFormat));
  }
  return Promise.resolve(labels);
}
async function _prepareLabelsUsingExecutor(featureSet, labelFormat) {
  const execResults = await Promise.all(
    featureSet.map(async (feature) => {
      return labelFormat.executeAsync({ $feature: feature });
    })
  );
  const labels = execResults.filter((result) => result?.text).map((result) => _cleanupLabel(result.text).split(lineSeparatorChar));
  return Promise.resolve(labels);
}
export {
  _prepareAttributeValue as _,
  downloadPDF as a,
  consolidateLabels as c,
  downloadCSV as d,
  removeDuplicateLabels as r
};
