/**
 * This object contains helper methods for generating data-driven visualizations with unique types (or categories)
 * based on a field value from features in a [Layer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-Layer.html).
 * The [createRenderer()](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#createRenderer) method generates a
 * [Renderer](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-Renderer.html) object that may be applied directly to the layer used to
 * generate it. This
 * renderer contains unique values with colors best suited to the view's background.
 *
 * > [!WARNING]
 * >
 * > **Known Limitations**
 * >
 * > [SceneLayers](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-SceneLayer.html) must have the `supportsRenderer` and `supportsLayerQuery` capabilities enabled unless a predefined [statistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-statistics-types.html#SummaryStatisticsResult) object is provided to the `statistics` parameter of the method. To check a SceneLayer's capabilities, use the [SceneLayer.getFieldUsageInfo()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-SceneLayer.html#getFieldUsageInfo) method.
 * > Generating renderers and visual variables using SQL expressions is currently restricted to feature services hosted on [ArcGIS Online](https://www.arcgis.com).
 *
 * @since 4.4
 * @see [Styles and data visualization](https://developers.arcgis.com/javascript/latest/visualization/)
 */
import type FeatureFilter from "../../layers/support/FeatureFilter.js";
import type PointCloudUniqueValueRenderer from "../../renderers/PointCloudUniqueValueRenderer.js";
import type UniqueValueRenderer from "../../renderers/UniqueValueRenderer.js";
import type { MapViewOrSceneView } from "../../views/MapViewOrSceneView.js";
import type { AbortOptions } from "../../core/promiseUtils.js";
import type { RendererLegendOptionsProperties } from "../../renderers/support/RendererLegendOptions.js";
import type { SupportedLayer } from "../types.js";
import type { UniqueValuesResult } from "../statistics/types.js";
import type { BasemapTheme, TypeScheme } from "../symbology/types.js";
import type { SymbolUnion } from "../../symbols/types.js";

/**
 * Generates a [Renderer](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-Renderer.html) that may be applied directly to a
 * [Layer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-Layer.html) that supports renderers. The
 * renderer contains unique symbols representing a string or a numeric value returned
 * from the indicated field.
 *
 * In most cases you will provide a `layer`, `view`, and `field` to generate this renderer.
 * This is a scenario in which
 * the values of the field aren't well known and the user doesn't know which colors
 * to use in the visualization. You can also use a `valueExpression` instead of a `field` to visualize
 * features based on a value returned from a script executed at runtime.
 *
 * @param parameters - Input parameters for generating symbols to represent unique types based on data
 * returned from a given field.
 * @returns Resolves to an instance of [RendererResult](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#RendererResult).
 * @example
 * let layer = new FeatureLayer({
 *   portalItem: { id: "5ce5374a461e45bab714b43ffedf151d" }
 * });
 *
 * // visualization based on categorical field
 * let typeParams = {
 *   layer: layer,
 *   view: view,
 *   field: "Party"
 * };
 *
 * // when the promise resolves, apply the visual variables to the renderer
 * typeRendererCreator.createRenderer(typeParams)
 *   .then(function(response){
 *     layer.renderer = response.renderer;
 *   });
 * @example
 * let layer = new FeatureLayer({
 *   url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/counties_politics_poverty/FeatureServer/0"
 * });
 *
 * // visualization based off Arcade expression
 * let typeParams = {
 *   layer: layer,
 *   valueExpression: "IIF($feature.DEMOCRAT > $feature.REPUBLICAN, 'Democrat', 'Republican', 'Tied')",
 *   view: view,
 *   valueExpressionTitle: "Election Winner"
 * };
 *
 * // when the promise resolves, apply the visual variables to the renderer
 * typeRendererCreator.createRenderer(typeParams)
 *   .then(function(response){
 *     layer.renderer = renderer;
 *   });
 */
export function createRenderer(parameters: RendererParameters): Promise<RendererResult>;

/**
 * Generates a [PointCloudUniqueValueRenderer](https://developers.arcgis.com/javascript/latest/api-reference/esri-renderers-PointCloudUniqueValueRenderer.html) based on
 * a given field of a [PointCloudLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-PointCloudLayer.html). This renderer visualizes
 * points of the same type, usually from the `CLASS_CODE` field or an equivalent field that stores
 * information related to the classification of the data (e.g. low vegetation, high vegetation, ground, etc.).
 * The generated renderer visualizes each point with a standard predefined color matching the class code of the point.
 *
 * All that's required is
 * a layer instance and field name. You can optionally set the size and density
 * of the points to suit the needs of the desired visualization.
 *
 * @param parameters - Input parameters for generating a renderer based on the given
 *   field of the input layer.
 * @returns Resolves to an object containing the renderer to set on the input layer. See
 *   [PCClassRendererResult](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#PCClassRendererResult) for more details.
 * @since 4.5
 * @example
 * let layer = new PointCloudLayer({
 *   url: "https://tiles.arcgis.com/tiles/V6ZHFr6zdgNZuVG0/arcgis/rest/services/BARNEGAT_BAY_LiDAR_UTM/SceneServer"
 * });
 *
 * let parameters = {
 *   layer: layer,
 *   field: "CLASS_CODE"
 * };
 *
 * // when the promise resolves, apply the renderer to the layer
 * typeRendererCreator.createPCClassRenderer(parameters)
 *   .then(function(response){
 *     layer.renderer = response.renderer;
 *   });
 */
export function createPCClassRenderer(parameters: PCClassRendererParameters): Promise<PCClassRendererResult>;

export type SymbolType = "2d" | "3d-flat" | "3d-volumetric" | "3d-volumetric-uniform";

export type ColorMixModeType = "tint" | "replace" | "multiply";

export interface RendererParameters extends AbortOptions {
  /**
   * The layer for which the renderer
   * is generated. When a client-side layer type is provided, attribute and spatial statistics are calculated
   * only from features in the view's extent. When a server-side layer type is provided, the statistics
   * are calculated from the entire layer, unless a `valueExpression` is provided.
   */
  layer?: SupportedLayer | null;
  /**
   * The name of the field from which to extract unique values that will be used for
   * the basis of the data-driven visualization.
   *   This property is ignored if a `valueExpression` is used.
   */
  field?: string | null;
  /**
   * Specifies the name of a second attribute field used to categorize features. All
   *   combinations of `field`, `field2`, and `field3` values are unique categories and
   *   may have their own symbol. This property is ignored if a `valueExpression` is used.
   *
   * @since 4.25
   */
  field2?: string | null;
  /**
   * Specifies the name of a third attribute field used to categorize features. All
   *   combinations of `field`, `field2`, and `field3` values are unique categories and
   *   may have their own symbol. This property is ignored if a `valueExpression` is used.
   *
   * @since 4.25
   */
  field3?: string | null;
  /**
   * An [Arcade](https://developers.arcgis.com/javascript/latest/arcade/) expression following
   * the specification defined by the [Arcade Visualization Profile](https://developers.arcgis.com/javascript/latest/arcade/#visualization).
   * Expressions may reference field values using the `$feature` profile variable and must return
   * a string or a number. This property overrides the `field` property and therefore is used instead of an input `field` value.
   * The `view` parameter is required if specifying a `valueExpression`. When using a `valueExpression`, client-side
   * statistics are calculated based on the features in the view's extent.
   */
  valueExpression?: string | null;
  /**
   * Text describing the value returned from the `valueExpression`.
   * This is used by the [Legend](https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-legend/).
   */
  valueExpressionTitle?: string | null;
  /**
   * Provides options for setting a title to a field that will override the field
   * alias defined in the service. This title will represent the field in the [Legend](https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-legend/).
   */
  legendOptions?: RendererLegendOptionsProperties | null;
  /**
   * The number of types (or categories) displayed by the renderer.
   * Use `-1` to display all returned types.
   *
   * @default 10
   */
  numTypes?: number | null;
  /**
   * In authoring apps,
   * the user may select a pre-defined type scheme. Pass the scheme object to this property to avoid getting one
   * based on the view's background.
   */
  typeScheme?: TypeScheme | null;
  /**
   * Enables the `defaultSymbol` on the renderer and assigns it to features
   * with no value.
   */
  defaultSymbolEnabled?: boolean | null;
  /**
   * Indicates how values should be sorted in the [Legend](https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-legend/).
   * See the table below for information about values that may be passed to this parameter.
   * Possible Value | Description
   * ---------------|------------
   * count | Unique values/types will be sorted from highest to lowest based on the count of features that fall in each category.
   * value | Unique values/types will be sorted in alphanumeric order.
   * none | _Since 4.28_ Unique values/types will be returned in the same order they are defined in the `statistics` parameter or returned from the uniqueValues statistics query (if the `statistics` parameter is not defined).
   *
   * @default count
   */
  sortBy?: SortBy | null;
  /**
   * For polygon layers only. Indicates whether the
   *   polygon outline width should vary based on view scale. When set, a valid [MapView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html)
   *   instance must be provided in the `view` parameter. This option is not supported for 3D
   *   [SceneViews](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html).
   *
   * @default false
   */
  outlineOptimizationEnabled?: boolean | null;
  /**
   * For point and polyline layers only. Indicates whether
   *   symbol sizes should vary based on view scale. When set, a valid [MapView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html)
   *   instance must be provided in the `view` parameter. This option is not supported for 3D
   *   [SceneViews](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html).
   *
   * @default false
   */
  sizeOptimizationEnabled?: boolean | null;
  /**
   * A statistics object generated from the [uniqueValues](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-statistics-uniqueValues.html) function.
   * If statistics for the field have already been generated, then pass the object here to avoid making a second statistics
   * query to the server.
   */
  statistics?: UniqueValuesResult | null;
  /**
   * Indicates whether the generated renderer is for a binning or clustering visualization.
   *   If `true`, then the input field(s) in this method should refer to [aggregate fields](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-support-AggregateField.html) defined in the `featureReduction` property of the layer.
   */
  forBinning?: boolean | null;
  /**
   * When defined, only features included in the filter
   *   are considered in the attribute and spatial statistics calculations when determining the final renderer.
   *   This is useful when a lot of variation exists in the data
   *   that could result in undesired data ranges. A common use case would be to set a filter that only
   *   includes features in the current extent of the view where the data is most likely to be viewed. Currently, only
   *   geometry filters with an `intersects` spatial relationship are supported. All other filter types (including `where`) are ignored.
   *
   * @since 4.31
   */
  filter?: FeatureFilter | null;
  /**
   * The type of symbol to generate. This depends on the view
   * in which you are working and the desired visualization. This parameter can be ignored for layers
   * with a `mesh` geometry type. Possible values are described below.
   *
   * | Value | Description |
   * | ----- | ----------- |
   * | 2d | Generates a visualization using 2D symbols such as [SimpleMarkerSymbol](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-SimpleMarkerSymbol.html), [SimpleLineSymbol](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-SimpleLineSymbol.html), or [SimpleFillSymbol](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-SimpleFillSymbol.html). Use this option if generating a visualization for data in a [MapView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html). |
   * | 3d-flat | Generates a visualization using 3D symbols with flat symbol layers such as [IconSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-IconSymbol3DLayer.html), [LineSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-LineSymbol3DLayer.html), or [FillSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-FillSymbol3DLayer.html). Use this option if generating a 2D visualization for data in a [SceneView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html). |
   * | 3d-volumetric | Generates a visualization using 3D symbols with volumetric symbol layers such as [ObjectSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-ObjectSymbol3DLayer.html), [PathSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-PathSymbol3DLayer.html), or [ExtrudeSymbol3DLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-ExtrudeSymbol3DLayer.html). Use this option if generating a 3D visualization for data in a [SceneView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html) and only the symbol's height should be variable, for example with cylinders. A SceneView instance must be provided to the `view` parameter if this option is used. |
   * | 3d-volumetric-uniform | Generates a visualization using uniformly-sized 3D symbols with volumetric symbol layers. Use this option if generating a 3D visualization for data in a [SceneView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html) and the symbol should be sized uniformly, for example with spheres. A SceneView instance must be provided to the `view` parameter if this option is used. |
   *
   * @default 2d
   */
  symbolType?: SymbolType | null;
  /**
   * **This option only applies to generating renderers for mesh SceneLayers**.
   * Specifies how the symbol's color is applied to the geometry color/texture. See the documentation in
   * [FillSymbol3DLayer.material](https://developers.arcgis.com/javascript/latest/api-reference/esri-symbols-FillSymbol3DLayer.html#material) for more context.
   * See the table below for possible values.
   *
   * Value | Description
   * ------|------------
   * tint | Applies the symbol `color` to the desaturated geometry/texture color.
   * replace | Removes the geometry/texture color and applies the symbol `color`.
   * multiply | Multiplies geometry/texture color value with the symbol `color` value. The result is a darker color. Multiplying with white keeps the geometry color the same.
   *
   * @default replace
   */
  colorMixMode?: ColorMixModeType | null;
  /**
   * The view where the input layer is rendered. This method
   *   inspects the view's background (i.e. basemap, web map background, or view container) to determine optimal
   *   colors for the output renderer. This parameter should always be set in practice, but if not provided this method
   *   will assume the generated renderer will display on a light background. This parameter is required when creating
   *   renderers using a `valueExpression` or for renderers intended for display in a SceneView.
   */
  view?: MapViewOrSceneView | null;
  /**
   * Indicates that all domain codes should be returned if the given field
   *   has domain values.
   */
  returnAllCodedValues?: boolean | null;
}

export type SortBy = "count" | "value" | "none";

/**
 * The result object of the [createRenderer()](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#createRenderer) method. See the table
 * below for details of each property.
 */
export interface RendererResult {
  /**
   * The renderer object configured to best
   * match the background of the view. Set this on a layer's `renderer` property to
   * update its visualization.
   */
  renderer: UniqueValueRenderer;
  /**
   * An array of objects describing the value, symbol, and count of
   * each unique type or category represented in the renderer.
   */
  uniqueValueInfos: UniqueValueInfo[];
  /**
   * An array of objects describing the values or categories
   * excluded from consideration in visualizing data from the given field. The specification of each object matches
   * that of the objects specified in the `uniqueValueInfos` property.
   */
  excludedUniqueValueInfos: UniqueValueInfo[];
  /**
   * The scheme used to
   * represent each category within the `uniqueValueInfos`.
   */
  typeScheme: TypeScheme;
  /**
   * The ID of the basemap used to determine the optimal color scheme to represent the
   * categorical variable.
   */
  basemapId?: string | null;
  /** Indicates whether the average color of the input view's basemap is `light` or `dark`. */
  basemapTheme?: BasemapTheme | null;
}

/** Describes the symbol, label, and value of a unique value generated by the [createRenderer()](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#createRenderer) method. */
export interface UniqueValueInfo {
  /** A unique value representing a type or category of features in a layer. */
  value: string | number;
  /**
   * The number of features with the given `value` (or belonging
   *   to the given category).
   */
  count: number;
  /**
   * The label describing the value or category in the
   *   [Legend](https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-legend/).
   */
  label: string;
  /**
   * The symbol used to represent features belonging to the
   *   given category.
   */
  symbol: SymbolUnion;
}

export interface PCClassRendererParameters extends AbortOptions {
  /**
   * The layer for which the visualization
   *   is generated.
   */
  layer?: SupportedLayer | null;
  /**
   * The name of the field containing classification data for the given layer.
   *   A common field name used for this renderer type is `CLASS_CODE`, though other fields can be used.
   */
  field?: string | null;
  /**
   * In authoring apps,
   * the user may select a pre-defined type scheme. Pass the scheme object to this property to avoid getting one
   * based on the `basemap`.
   */
  typeScheme?: TypeScheme | null;
  /**
   * The size of each point expressed as a percentage. This value will determine point sizes
   * scaled based on the given `density` of points. When the value is `100%`, the size
   * of each point is set so that it minimizes the number of gaps between neighboring points. Any value above `100%`
   * will allow for points to overlap neighboring points scaled to the given value. Values below `100%` scale point
   * sizes smaller so there appear to be more gaps between points.
   *
   * @default 100%
   */
  size?: string | null;
  /**
   * The number of points per inch in the view.
   *
   * @default 25
   */
  density?: number | null;
  /**
   * A statistics object generated from the [uniqueValues](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-statistics-uniqueValues.html) function.
   * If statistics for the field have already been generated, then pass the object here to avoid making a second statistics
   * query to the server.
   */
  statistics?: UniqueValuesResult | null;
}

/**
 * The result object of the [createPCClassRenderer()](https://developers.arcgis.com/javascript/latest/api-reference/esri-smartMapping-renderers-type.html#createPCClassRenderer) method. See the table
 * below for details of each property.
 */
export interface PCClassRendererResult {
  /**
   * The renderer object configured to
   * represent the class codes in the point cloud. Set this object on the input layer's `renderer` property to
   * update its visualization.
   */
  renderer: PointCloudUniqueValueRenderer;
}