import type Extent from "../../geometry/Extent.js";
import type Multipoint from "../../geometry/Multipoint.js";
import type Point from "../../geometry/Point.js";
import type Polyline from "../../geometry/Polyline.js";
import type CodedValueDomain from "./CodedValueDomain.js";
import type ControlPoint from "./ControlPoint.js";
import type DateTimeFieldFormat from "./DateTimeFieldFormat.js";
import type DimensionalDefinition from "./DimensionalDefinition.js";
import type InheritedDomain from "./InheritedDomain.js";
import type NumberFieldFormat from "./NumberFieldFormat.js";
import type ParquetGeometryEncodingLocation from "./ParquetGeometryEncodingLocation.js";
import type ParquetGeometryEncodingWkb from "./ParquetGeometryEncodingWkb.js";
import type PixelBlock from "./PixelBlock.js";
import type RangeDomain from "./RangeDomain.js";
import type { AbortOptions } from "../../core/promiseUtils.js";
import type { GroundElevationLayerUnion, QueryRelatedCapabilities } from "../types.js";

export type FieldType = "small-integer" | "integer" | "single" | "double" | "long" | "string" | "date" | "oid" | "geometry" | "blob" | "raster" | "guid" | "global-id" | "xml" | "big-integer" | "date-only" | "time-only" | "timestamp-offset";

export type FieldValueType = "binary" | "coordinate" | "count-or-amount" | "currency" | "date-and-time" | "description" | "email-address" | "location-or-place-name" | "measurement" | "name-or-title" | "none" | "ordered-or-ranked" | "percentage-or-ratio" | "phone-number" | "type-or-category" | "unique-identifier";

export type FieldFormatUnion = DateTimeFieldFormat | NumberFieldFormat;

export type DomainUnion = RangeDomain | CodedValueDomain | InheritedDomain;

export type ControlPoints = [
    ControlPoint,
    ControlPoint
] | [
    ControlPoint,
    ControlPoint,
    ControlPoint
] | [
    ControlPoint,
    ControlPoint,
    ControlPoint,
    ControlPoint
];

export type PlacementType = "above-center" | "above-left" | "above-right" | "center-center" | "center-left" | "center-right" | "below-center" | "below-left" | "below-right" | "always-horizontal" | "above-along" | "below-along" | "center-along";

export type UnsupportedPlacementType = "above-after" | "above-before" | "above-start" | "above-end" | "below-after" | "below-before" | "below-start" | "below-end" | "center-after" | "center-before" | "center-start" | "center-end" | "always-horizontal";

export type PlacementType3D = "above-center" | "above-left" | "above-right" | "center-center" | "center-left" | "center-right" | "below-center" | "below-left" | "below-right";

/**
 * Data series returned in [RasterIdentifyResult](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-support-types.html#RasterIdentifyResult) when [ImageryTileLayer#identify()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryTileLayer.html#identify) is called on a
 * transposed multidimensional ImageryTileLayer.
 *
 * @since 4.25
 */
export interface RasterSliceValue {
  /** The magnitude and direction values for services containing vector data. */
  magdirValue?: [
      number,
      number
  ];
  /** A dimensional definition associated with a given slice. */
  multidimensionalDefinition: DimensionalDefinition[];
  /** Image service pixel values for a given slice. */
  value?: number[] | null;
}

/**
 * The result of `identify` operation on [ImageryTileLayer#identify()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryTileLayer.html#identify) and [WCSLayer#identify()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-WCSLayer.html#identify).
 * It includes the processed pixel values for a given location.
 */
export interface RasterIdentifyResult {
  /**
   * Transposed multidimensional data for the requested location and variable
   * returned from a transposed multidimensional [ImageryTileLayer#identify()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryTileLayer.html#identify). Added since version 4.25.
   */
  dataSeries?: RasterSliceValue[];
  /** The identified location. */
  location: Point;
  /** The processed pixel values showing results of the [ImageryTileLayer#rasterFunction](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryTileLayer.html#rasterFunction) applied to the layer. Added since version 4.26. */
  processedValue?: number[];
  /** Image service pixel values. */
  value?: number[] | null;
}

/**
 * An object that provides the user access to
 * [PixelBlock#pixels](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-support-PixelBlock.html#pixels) and their values in the layer.
 * See the object specifications table below for details about each property. This object
 * is used as input in the [ImageryLayer#pixelFilter](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html#pixelFilter) function
 * for filtering and processing each pixel in the block.
 */
export interface PixelData {
  /** The extent of the `pixelBlock`. */
  extent: Extent;
  /**
   * An object representing
   *   the pixels in the view. This provides the user access to each pixel on the client
   *   via the pixels property of this object.
   */
  pixelBlock?: PixelBlock | null;
}

/**
 * Raster statistics information returned that meets the specified [ImageHistogramParameters](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-ImageHistogramParameters.html)
 * from the `computeStatisticsHistograms()` method on [ImageryLayer#computeStatisticsHistograms()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html#computeStatisticsHistograms) or [ImageryTileLayer#computeStatisticsHistograms()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryTileLayer.html#computeStatisticsHistograms).
 *
 * @since 4.18
 */
export interface RasterBandStatistics {
  /** Average of the statistics. */
  avg?: number;
  /** Count of the statistics. */
  count?: number;
  /** Maximum value of the statistics. */
  max: number;
  /** Median value of the statistics. */
  median?: number;
  /** Minimum value of the statistics. */
  min: number;
  /** Mode value of the statistics. */
  mode?: number;
  /** Standard deviation of the statistics. */
  stddev?: number;
  /** Sum of the statistics. */
  sum?: number;
}

/**
 * Raster histogram information returned that meets the specified [ImageHistogramParameters](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-ImageHistogramParameters.html)
 * from the [ImageryLayer#computeHistograms()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html#computeHistograms) or [ImageryLayer#computeStatisticsHistograms()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html#computeStatisticsHistograms) method.
 *
 * @since 4.18
 */
export interface RasterHistogram {
  /** Count of pixels that fall into each bin. */
  counts: number[] | Uint32Array;
  /** The maximum pixel value of the histogram. Matches the maximum bound of the last bin. */
  max: number;
  /** The minimum pixel value of the histogram. Matches the minimum bound of the first bin. */
  min: number;
  /** Number of bins. */
  size: number;
}

export interface RasterDimensionalInfo {
  /** Dimension description. */
  description?: string | null;
  /** The extent of dimension values. */
  extent?: number[];
  /** Indicates if each dimension value is represented using a range. */
  hasRanges?: boolean | null;
  /** Indicates if the dimension is recorded at regular intervals. */
  hasRegularIntervals?: boolean | null;
  /** Dimension interval. */
  interval?: number | null;
  /** Dimension interval unit. */
  intervalUnit?: string | null;
  /** Dimension name. */
  name: string;
  /** Indicates if the dimension is recurring, e.g. data with 12 slices each represents aggregated value per month over the years. */
  recurring?: boolean | null;
  /** Dimension unit. */
  unit?: string | null;
  /** An array of single values or tuples [min, max] each defining a range of valid values along the specified dimension. */
  values?: (number | [
      number,
      number
  ])[] | null;
}

/**
 * RasterMultidimensionalInfo contains dimensions for each variable in the service  describing information about the images collected at multiple times, depths, or heights.
 *
 * @example
 * // update the statistics of the layer's stretch renderer.
 * const renderer = layer.renderer.clone();
 *  const dimensions = layer.rasterInfo.multidimensionalInfo;
 * // get the salinity variable's statistics
 * const salinity = dimensions.variables.find((variable) => variable.name === variableName);
 * renderer.customStatistics = salinity.statistics;
 * layer.renderer = renderer;
 */
export interface RasterMultidimensionalInfo {
  /**
   * The multi dimensional variables. It stores information such as name, unit and dimensions. For example, a temperature variable can store
   * temperature data and the salinity variable can store the salinity data measured daily at different depths.
   */
  variables: RasterMultidimensionalInfoVariable[];
}

export interface RasterMultidimensionalInfoVariable {
  /** Variable description. */
  description?: string | null;
  /**
   * A dimension may be used to represent real physical dimensions such as time or depth/height. It may also be used to represent more
   * abstract quantities such as station id or station-time pair. For example, if your temperature data has a corresponding Date dimension field representing the day it was captured, and
   * your salinity data has a Depth dimension field representing the depth at which it was measured, the Dimensions field for that variable would be Date and Depth.
   */
  dimensions: RasterDimensionalInfo[];
  /** Variable histograms. */
  histograms?: RasterHistogram[] | null;
  /** Variable name. */
  name: string;
  /** Variable statistics. */
  statistics?: RasterBandStatistics[] | null;
  /** Unit of the variable measured in. */
  unit?: string | null;
}

/**
 * The raster function published with the image service.
 *
 * @since 4.22
 */
export interface RasterFunctionInfo {
  /** Description of the raster function. */
  description: string;
  /** The function type. Possible values: //0 = Mosaic, 1 = Item, 2 = Item Group. */
  functionType: number;
  /** Additional info describing what the raster function does. */
  help: string;
  /** Name of the raster function. */
  name: string;
  /** Inline base64 encoded thumbnail of the raster function. */
  thumbnail: string;
}

export interface SublayerCapabilities {
  /** **Since 4.32.** Describes attachment capabilities that can be performed on the features in the layer. */
  attachment: SublayerCapabilitiesAttachment;
  /** Describes characteristics of the data in the layer. */
  data: SublayerCapabilitiesData;
  /** Describes operations that can be performed on features in the layer. */
  operations: SublayerCapabilitiesOperations;
  /** Indicates if the layer's query operation supports querying features or records related to features in the layer. */
  queryRelated: SublayerCapabilitiesQueryRelated;
}

export interface SublayerCapabilitiesAttachment {
  /**
   * **Since 4.32.** Indicates if resized attachments are supported in the layer.
   * This is useful for showing thumbnails in [Popups](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Popup.html).
   */
  supportsResize: boolean;
}

export interface SublayerCapabilitiesData {
  /** Indicates if attachments are enabled on the layer. */
  supportsAttachment: boolean;
}

export interface SublayerCapabilitiesOperations {
  /** Indicates if features in the layer can be [queried](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryFeatures). */
  supportsQuery: boolean;
  /**
   * Indicates if the layer supports
   * [REST API queryAttachments](https://developers.arcgis.com/rest/services-reference/query-attachments-feature-service-layer-.htm)
   * operation.
   * If `false`, [queryAttachments() method](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryAttachments) can only return attachments for one feature at a time.
   * If `true`, `queryAttachments()` can return attachments for array of [AttachmentQuery#objectIds](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-AttachmentQuery.html#objectIds).
   */
  supportsQueryAttachments: boolean;
}

export type SublayerCapabilitiesQueryRelated = Omit<QueryRelatedCapabilities, "supportsCacheHint">;

export type I3SGeometryType = "point" | "mesh";

export interface SceneServiceVersion {
  /** The major version of the scene layer. */
  major: number;
  /** The minor version of the scene layer. */
  minor: number;
  /** The complete version string of the scene layer. */
  versionString: string;
}

/**
 * A description of each image contained in the sprite. It describes the dimension and pixel ratio of each image and its location within the sprite image.
 * This information is saved in the JSON index file containing description of each image.
 *
 * @since 4.25
 */
export interface SpriteInfo {
  /** The height of the sprite. */
  height: number;
  /** The pixel ratio of the sprite. */
  pixelRatio?: number;
  /** Whether the sprite is SDF or not. */
  sdf?: boolean;
  /** The width of the sprite. */
  width: number;
  /** The x position of the upper-left corner of the sprite. */
  x: number;
  /** The y position of the upper-left corner of the sprite. */
  y: number;
}

export type Image = HTMLImageElement | HTMLCanvasElement | ImageBitmap | ImageData | ImageObject;

/** Defines an image object that can be used when setting [SpriteSourceImageInfo](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-support-types.html#SpriteSourceImageInfo). */
export interface ImageObject {
  /** The image data for the sprite. */
  data: ArrayBuffer;
  /** The height of the sprite. */
  height: number;
  /** The width of the sprite. */
  width: number;
}

/**
 * The URL of the sprite source to be fetched when the [VectorTileLayer#setSpriteSource()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-VectorTileLayer.html#setSpriteSource) method is called.
 *
 * @since 4.25
 * @example
 * // update the layer's sprite resources by fetching sprite resources
 * // from the given url
 * const spriteUrl = "link-to-your-sprite-source";
 * vtLayer.setSpriteSource({
 *   type: "url",
 *   spriteUrl,
 *   pixelRatio: window.devicePixelRatio
 *  }).then((results)=>{
 *   console.log("setSpriteSource results", results);
 *   // do additional style updates here
 *   // change the background layer's sprite
 *   const sl = vtLayer.getStyleLayer("background");
 *   sl.paint["background-pattern"] =
 *     sl.paint["background-pattern"] === "Polygon" ? "Waves"  : "Polygon";
 *   vtLayer.setStyleLayer(sl);
 * });
 */
export interface SpriteSourceUrlInfo {
  /** The device pixel ratio used to fetch the appropriate sprite source. */
  pixelRatio?: number;
  /** The sprite image format. */
  spriteFormat?: "png" | "webp";
  /** The sprite source url. */
  spriteUrl: string;
  /** For SpriteSourceUrlInfo the type is always "url". */
  type: "url";
}

/**
 * Defines sprite source from an image and index json when the [VectorTileLayer#setSpriteSource()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-VectorTileLayer.html#setSpriteSource) method is called.
 *
 * @since 4.25
 * @example
 * // set sprites source from an image info directly on the layer
 * fetchSprites(spriteUrl, pixelRatio).then((params) => {
 *   vtLayer.setSpriteSource(params).then((results)=>{
 *     console.log("setSpriteInfo results", results);
 *     const sl = vtLayer.getStyleLayer("background");
 *     sl.paint["background-pattern"] =
 *       sl.paint["background-pattern"] === "Polygon" ? "Waves"  : "Polygon";
 *     vtLayer.setStyleLayer(sl);
 *   });
 * });
 *
 * async function fetchSprites(url, pixelRatio) {
 *   // get the index and image files from specified location
 *   const jsonUrl = `${url}${pixelRatio > 1.25 ? "@2x" : ""}.json`;
 *   const imageUrl = `${url}${pixelRatio > 1.25 ? "@2x" : ""}.png`;
 *
 *   const [jsonResult, imageResult] = await Promise.all([
 *     esriRequest(jsonUrl, { responseType: "json" }),
 *     esriRequest(imageUrl, { responseType: "image" })
 *   ]);
 *
 *   // load the appropriate files depending on device pixel ratio
 *   const spriteSource = `spriteSource${pixelRatio > 1.25 ? "2" : "1"}x`;
 *
 *   return new SpriteSourceImageInfo({
 *     type: "image",
 *     [spriteSource]: {
 *       image: imageResult.data,
 *       json: jsonResult.data
 *     },
 *     spriteUrl: url
 *   });
 * }
 */
export interface SpriteSourceImageInfo {
  /** The device pixel ratio of the sprite source. */
  pixelRatio?: number;
  /** Sprite source for non high-DPI devices. */
  spriteSource1x?: SpriteSourceImageInfoSpriteSource1x;
  /** Sprite source for high-DPI devices. */
  spriteSource2x?: SpriteSourceImageInfoSpriteSource2x;
  /** The sprite source url. */
  spriteUrl?: string;
  /** For SpriteSourceImageInfo the type is always "image". */
  type: "image";
}

export interface SpriteSourceImageInfoSpriteSource1x {
  /** Image containing sprite data for non high-DPI devices. */
  image: Image;
  /** The JSON representation of an index file containing a description of each image in the sprite for non high-DPI devices. */
  json: Record<string, SpriteInfo | undefined>;
}

export interface SpriteSourceImageInfoSpriteSource2x {
  /** Image containing sprite data for high-DPI devices. */
  image: Image;
  /** The JSON representation of an index file containing a description of each image in the sprite for high-DPI devices. */
  json: Record<string, SpriteInfo | undefined>;
}

export type SpriteSourceInfo = SpriteSourceUrlInfo | SpriteSourceImageInfo;

export type StreamConnectionStatus = "disconnected" | "connected";

export interface BaseCapabilities {
  /** Indicates whether specific operation or capability is enabled on the editable layer. */
  enabled: boolean;
}

export interface OwnerAdminCapabilities extends BaseCapabilities {
  /** Indicates when editing is enabled and is *only* due to administrative permissions. It returns `false` for *any* users if editing is generally enabled. */
  reliesOnOwnerAdminPrivileges: boolean;
}

/** Specific permissions for create operations. It takes into account the [Editor's layerInfo](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Editor-types.html#LayerInfo) settings. */
export interface CreateCapabilities extends OwnerAdminCapabilities {
  /** The editing capabilities for attachments. */
  attachments: AttachmentCapabilities;
  /** Indicates whether attributes can be created on the editable layer. */
  attributes: boolean;
  /** Indicates whether a geometry can be created on the editable layer. */
  geometry: boolean;
}

/** Specific permissions for update operations. It takes into account the [Editor's layerInfo](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Editor-types.html#LayerInfo) settings. */
export interface UpdateCapabilities extends OwnerAdminCapabilities {
  /** The editing capabilities for attachments. */
  attachments: UpdateAttachmentCapabilities;
  /** Indicates whether attributes can be updated on the editable layer. */
  attributes: boolean;
  /** Indicates whether a geometry can be updated on the editable layer. */
  geometry: boolean;
}

/** Specific permissions for delete operations. Accounts for layerInfo settings. */
export interface DeleteCapabilities extends OwnerAdminCapabilities {}

/** The editing capabilities for attachments. */
export interface AttachmentCapabilities extends BaseCapabilities {}

export interface UpdateAttachmentCapabilities extends AttachmentCapabilities {}

/**
 * Specific permissions for relationship operations. It takes into account the [Editor's layerInfo](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Editor-types.html#LayerInfo) settings.
 * If enabled, indicates that the layer contains at least one relationship, in addition to having child [Workflow](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Editor-Workflow.html) permissions.
 */
export interface RelationshipCapabilities extends BaseCapabilities {}

/**
 * An array of [Exchangeable image file format](https://en.wikipedia.org/wiki/Exif) information
 * for the attachment. You must set the attachment query's [AttachmentQuery#returnMetadata](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-AttachmentQuery.html#returnMetadata) to `true` to get the `exif`
 * info associated with the attachment.
 */
export interface ExifInfo {
  /** The file name. */
  name: string;
  /** Array of tag objects containing the following properties: */
  tags?: ExifTag[];
}

export interface ExifTag {
  /** The tag description. */
  description: string;
  /** The tag name. */
  name: string;
  /** The value of the tag. */
  value: any;
}

export type ParquetGeometryEncodingUnion = ParquetGeometryEncodingWkb | ParquetGeometryEncodingLocation;

export interface CreateElevationSamplerOptions extends AbortOptions {
  /**
   * Controls the horizontal resolution (cell size) in meters
   *   from which elevation data is sampled (defaults to `auto`). See the table below for more details on the different
   *   settings.
   *
   * demResolution          | Description
   * -----------------------|-------------
   * `auto`                 | Automatically chooses an appropriate resolution for each coordinate of the input geometry. The current implementation will try to use the finest available resolution given that the total required number of tile requests does not exceed a certain maximum amount (currently 20). Note that this may result in values being sampled from different resolutions.
   * `finest-contiguous`    | Sample elevation from the finest available resolution (cell size) across the entire geometry.
   * `{number}`             | Sample elevation from the resolution closest to the specified resolution (in meters).
   *
   * @default auto
   */
  demResolution?: ElevationQueryDemResolution;
  /**
   * The value that appears in the resulting geometry when there
   *   is no data available.
   *
   * @default 0
   */
  noDataValue?: number;
}

export interface ElevationQueryOptions extends CreateElevationSamplerOptions {
  /**
   * Indicates whether to return additional sample information
   *   for each sampled coordinate.
   *
   * @default false
   */
  returnSampleInfo?: boolean;
}

export type ElevationQueryDemResolution = number | "finest-contiguous" | "auto";

/** Supported geometries to query elevation for. */
export type ElevationQueryGeometry = Point | Multipoint | Polyline;

/** Object returned when [ElevationLayer#queryElevation()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ElevationLayer.html#queryElevation), [Ground#queryElevation()](https://developers.arcgis.com/javascript/latest/api-reference/esri-Ground.html#queryElevation), or [Ground#queryElevation()](https://developers.arcgis.com/javascript/latest/api-reference/esri-Ground.html#queryElevation) promise resolves: */
export interface ElevationQueryResult<GeometryType> {
  /** The geometry with sampled z-values. */
  geometry: GeometryType;
  /** The value used when there is no data available. */
  noDataValue?: number | null;
  /**
   * Contains additional information about how the geometry was sampled.
   *                                   This property is present depending on whether the user set `options.returnSampleInfo = true`,
   *                                   for each coordinate in the geometry.
   */
  sampleInfo?: ElevationQuerySampleInfo[];
}

export interface ElevationQuerySampleInfo {
  /**
   * The resolution at which the z-value was sampled.
   *                                   If no data was available for sampling for a given coordinate,
   *                                   the dem resolution value will be -1 for that coordinate.
   */
  demResolution: number;
  /**
   * The elevation source from which
   *                                   the data for the corresponding coordinate was sampled.
   */
  source?: GroundElevationLayerUnion;
}