/**
 * Utilities for generating small preview images of symbols.
 *
 * @since 4.11
 */
import type Color from "../../Color.js";
import type Graphic from "../../Graphic.js";
import type UniqueValueRenderer from "../../renderers/UniqueValueRenderer.js";
import type SimpleLineSymbol from "../SimpleLineSymbol.js";
import type { MapViewOrSceneView } from "../../views/MapViewOrSceneView.js";
import type { GetDisplaySymbolOptions } from "../../renderers/types.js";
import type { SymbolUnion } from "../types.js";

/**
 * Generates an HTML element representing the legend of a [PieChartRenderer](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-PieChartRenderer.html). Use this method
 * to display the pie chart legend element in a DOM element.
 *
 * @param colors - A list of colors used to render the pie chart.
 * @param options - Options for creating the legend element.
 * @returns Returns the of the pie chart legend element to display in the DOM.
 * @since 4.26
 * @example
 * const { attributes, size, holePercentage, outline } = layer.renderer;
 * const colors = attributes.map( attribute => attribute.color );
 *
 * const element = symbolUtils.renderPieChartPreviewHTML(colors, {
 *   radius: size * 0.5,
 *   holePercentage,
 *   values: [10,70,20],
 *   outline
 * });
 *
 * legendElement.appendChild(element);
 */
export function renderPieChartPreviewHTML(colors: Color[], options?: RenderPieChartPreviewHTMLOptions): HTMLElement | null | undefined;

export interface RenderPieChartPreviewHTMLOptions {
  /**
   * The hole percentage of the chart on a scale of 0-1. A percentage of `0` creates
   *   a pie chart. Any other value creates a donut chart. This value indicates the percentage of the chart to
   *   remove from the center.
   *
   * @default 0
   */
  holePercentage?: number | null;
  /** The outline of the pie chart. */
  outline?: SimpleLineSymbol | null;
  /**
   * The radius of the pie chart in pixels.
   *
   * @default 40
   */
  radius?: number | null;
  /**
   * The values used to indicate the area each color should represent as a
   *   percentage when creating the pie chart. The number of values should match
   *   the number of colors. Value positions correspond with the positions of colors in the `colors` array. For example,
   *   if the first color in the `colors` array is red and the first value in the `values` array is 50, then 50% of
   *   the chart should be represented with red. The sum of all values defined here must equal 100. By default
   *   all colors are given an equal area.
   */
  values?: number[] | null;
}

/**
 * Generates an HTML element representing the square or diamond legend element of a relationship renderer. Use this method
 * to display the relationship legend element in a DOM element.
 *
 * @param renderer - A [relationship](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-relationship.html) renderer instance.
 * @param options - Options for creating the legend element.
 * @returns Returns the relationship legend element to display in the DOM.
 * @since 4.26
 * @example
 * const relationshipElement = symbolUtils.renderRelationshipRampPreviewHTML(layer.renderer);
 * body.appendChild(relationshipElement);
 */
export function renderRelationshipRampPreviewHTML(renderer: UniqueValueRenderer, options?: RenderRelationshipRampPreviewHTMLOptions): HTMLElement | null | undefined;

export interface RenderRelationshipRampPreviewHTMLOptions {
  /** The container node in which to place the generated relationship element. */
  node?: HTMLElement;
  /**
   * The opacity of the legend element.
   *
   * @default 1
   */
  opacity?: number;
}

/**
 * Generates a preview image of a color ramp to display in a DOM element.
 *
 * @param colors - An array of colors from which to construct the color ramp.
 * @param options - Formatting options for the color ramp.
 * @returns Returns a preview of the color ramp for display in the DOM.
 * @since 4.13
 * @example
 * const colors = [
 *   "#d6ffe1",
 *   "#8ceda6",
 *   "#2ee860",
 *   "#00e33d"
 * ];
 *
 * const colorRamp = symbolUtils.renderColorRampPreviewHTML(colors, {
 *   align: "vertical"
 * });
 *
 * body.appendChild(colorRamp);
 * @example
 * // Primary color scheme from colorSchemes.getSchemes()
 * const schemes = colorSchemes.getSchemes({
 *   basemap: "gray-vector",
 *   geometryType: "polygon",
 *   theme: "above-and-below"
 * });
 *
 * const colorRamp = symbolUtils.renderColorRampPreviewHTML(schemes.primaryScheme.colors, {
 *   align: "horizontal"
 * });
 *
 * body.appendChild(colorRamp);
 */
export function renderColorRampPreviewHTML(colors: Color[], options?: RenderColorRampPreviewHTMLOptions): HTMLElement;

export interface RenderColorRampPreviewHTMLOptions {
  /**
   * Specifies the alignment of the color ramp.
   *
   * @default vertical
   */
  align?: "horizontal" | "vertical" | null;
  /** An accessible label for the ramp element. */
  ariaLabel?: string | null;
  /** A CSS filter to apply to the ramp element. */
  cssEffectFilter?: string | null;
  /**
   * Indicates whether to render the color ramp with a continuous gradient.
   *   When `false`, distinct colors will appear in the ramp without a gradient.
   *
   * @default true
   */
  gradient?: boolean | null;
  /** The height of the ramp in pixels. */
  height?: number | null;
  /** The width of the ramp in pixels. */
  width?: number | null;
}

/**
 * Generates a preview image of a given symbol that can be displayed in
 * a DOM element.
 *
 * @param symbol - The symbol for which to generate a preview image.
 * @param options - Formatting options for the symbol preview image.
 * @returns Returns a preview of the given symbol to display to the DOM.
 * @example
 * // symbol from SimpleRenderer;
 * const symbol = layer.renderer.symbol.clone();
 *
 * symbolUtils.renderPreviewHTML(symbol, {
 *   node: document.getElementById("preview"),
 *   size: 8
 * });
 */
export function renderPreviewHTML<T extends RenderPreviewHTMLOptions>(symbol: SymbolUnion, options?: T): Promise<HTMLElement | null | undefined>;

/**
 * Returns a symbol representing the input [Graphic](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html). This method is useful when you need to know the exact visual properties
 * of a [Graphic's symbol](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html#symbol), particularly when the graphic comes from the result of a
 * [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) and its symbol properties may be empty. A symbol's properties won't be populated when a
 * [RendererUnion](https://developers.arcgis.com/javascript/latest/api-reference/esri-unionTypes.html#RendererUnion) defines the visualization of a layer rather than symbols set individually on each graphic of a layer.
 * This is the case for [FeatureLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html), and any other layer that has a `renderer` property.
 *
 * @param graphic - The graphic from which to retrieve the displayed symbol. This commonly comes
 *   from a [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) operation.
 * @param options - Options for generating the display symbol of the input graphic. These must be specified if the input
 *   graphic comes from a layer with a renderer that has [visual variables](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-SimpleRenderer.html#visualVariables)
 *   applied. See the object specification below.
 * @returns Returns the symbol representing the input graphic.
 * @example
 * view.on("click", async (event) => {
 *   const { results } = await view.hitTest(event, { include: layer });
 *   const graphic = results[0].graphic;
 *
 *   // do something with the result symbol
 *   const symbol = await symbolUtils.getDisplayedSymbol(graphic, {
 *     scale: view.scale,
 *     spatialReference: view.spatialReference,
 *     resolution: view.resolution
 *   });
 * });
 */
export function getDisplayedSymbol(graphic: Graphic, options?: GetDisplaySymbolOptions | null | undefined): Promise<SymbolUnion | null | undefined>;

/**
 * Returns a color representing the input [graphic's](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html) symbol. This method is useful when you
 * need to know the exact color of a [Graphic's symbol](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html#symbol), particularly when the graphic
 * comes from the result of a [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) and its symbol properties may be
 * empty. A symbol's properties won't be populated when a [Renderer](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-Renderer.html) defines the
 * visualization of a layer rather than symbols set individually on each graphic of a layer. This is the case for
 * [FeatureLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html), and any other layer that has a `renderer` property.
 *
 * @param graphic - The graphic from which to retrieve the displayed color. This commonly comes
 *   from a [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) or layer view query operation.
 * @param options - Options for generating the display color of the input graphic. These must be specified if the input
 *   graphic comes from a layer with a renderer that has [visual variables](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-SimpleRenderer.html#visualVariables)
 *   applied. See the object specification below.
 * @returns Returns the color representing the input graphic.
 * @since 4.19
 * @example
 * view.on("click", async (event) => {
 *   const { results } = await view.hitTest(event, { include: layer });
 *   const graphic = results[0].graphic;
 *
 *   // do something with the result color
 *   const color = await symbolUtils.getDisplayedColor(graphic);
 * });
 */
export function getDisplayedColor(graphic: Graphic, options?: GetDisplaySymbolOptions | null | undefined): Promise<Color | null | undefined>;

/**
 * Returns a label corresponding to the symbol of the input [Graphic](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html) as displayed in the [Legend](https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-legend/).
 *
 * @param graphic - The graphic from which to retrieve its associated label as displayed in the legend.
 *   This commonly comes from a [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) operation.
 * @param view - The graphic from which to retrieve the displayed symbol. This commonly comes
 *   from a [MapView#hitTest()](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#hitTest) operation.
 * @param options - Options for generating the legend label of the input graphic. These must be specified if the input
 *   graphic comes from a layer with a renderer that has [visual variables](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-SimpleRenderer.html#visualVariables)
 *   applied. See the object specification below.
 * @returns Returns the label representing the input graphic in the legend.
 * @since 4.29
 * @example
 * view.on("click", async (event) => {
 *   const { results } = await view.hitTest(event, { include: layer });
 *   const graphic = results[0].graphic;
 *
 *   // do something with the result symbol
 *   const label = await symbolUtils.getLegendLabel(graphic, view, {
 *     scale: view.scale,
 *     spatialReference: view.spatialReference,
 *     resolution: view.resolution
 *   });
 * });
 */
export function getLegendLabel(graphic: Graphic, view?: MapViewOrSceneView, options?: GetDisplaySymbolOptions | null | undefined): Promise<string | null | undefined>;

export interface RenderPreviewHTMLOptions {
  /** Indicates whether to disable upsampling for raster images. */
  disableUpsampling?: boolean | null;
  /** The maximum size of the symbol preview in points. */
  maxSize?: number | null;
  /** The parent node to append to the symbol. */
  node?: HTMLElement | null;
  /** The opacity of the layer represented by the `symbol`. Must be a number between 0 and 1. */
  opacity?: number | null;
  /** _Since 4.25_ The text that will be displayed in the symbol preview of a TextSymbol. This will override any existing text defined on the symbol. */
  overrideText?: string | null;
  /** The rotation of the symbol. */
  rotation?: number | null;
  /**
   * When `true` the size of the symbol preview will include the outline in the measurement of the entire symbol.
   *   When `false`, the symbol preview will not include the outline in the size measurement, thus matching the symbol size in the view.
   *
   * @default true
   */
  scale?: boolean | null;
  /**
   * Use a number to set the size (in points) of the symbol preview
   *   for any symbol representing a point. Starting at version 4.23, you can provide an object with
   *   width and height properties when creating symbol previews for simple symbols. At version 4.25, the width and height properties are supported on all symbols.
   */
  size?: number | RenderPreviewHTMLSizeOptions | null;
  /**
   * Options for setting the shape of a fill symbol preview. This can be a single string value (`tall`
   *   is the only option), or an object with config options. See the table below for specifics on the configuration options available.
   */
  symbolConfig?: "tall" | RenderPreviewSymbolConfigOptions | null;
}

export interface RenderPreviewHTMLSizeOptions {
  /**
   * The height of the symbol preview in points.
   *   Previews for SimpleFillSymbol must have `isSquareFill` enabled in `symbolConfig` for this
   *   property to take effect.
   */
  height: number;
  /**
   * The width of the symbol preview in points.
   *   Previews for SimpleFillSymbol must have `isSquareFill` enabled in `symbolConfig` for this
   *   property to take effect.
   */
  width: number;
}

export interface RenderPreviewSymbolConfigOptions {
  /**
   * Set to `true` to render the preview as a square instead of a generic polygon shape.
   *
   * @default false
   */
  isSquareFill?: boolean;
  /**
   * Set to `true` for "tall" symbols in portrait view. This is typically used for 3D cylinder object symbols.
   *
   * @default false
   */
  isTall?: boolean;
}