import type Multipoint from "../../geometry/Multipoint.js";
import type Point from "../../geometry/Point.js";
import type Polyline from "../../geometry/Polyline.js";
import type IntegratedMesh3DTilesLayer from "../IntegratedMesh3DTilesLayer.js";
import type CodedValueDomain from "./CodedValueDomain.js";
import type ControlPoint from "./ControlPoint.js";
import type DateTimeFieldFormat from "./DateTimeFieldFormat.js";
import type GeometryEncodingLocation from "./GeometryEncodingLocation.js";
import type GeometryEncodingWkb from "./GeometryEncodingWkb.js";
import type InheritedDomain from "./InheritedDomain.js";
import type NumberFieldFormat from "./NumberFieldFormat.js";
import type ParquetFilesData from "./ParquetFilesData.js";
import type ParquetPortalItemData from "./ParquetPortalItemData.js";
import type RangeDomain from "./RangeDomain.js";
import type { AbortOptions } from "../../core/promiseUtils.js";
import type { ElevationLayerUnion, QueryRelatedCapabilities } from "../types.js";

/**
 * Supported field types.
 *
 * @since 5.0
 */
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";

/**
 * Supported field value types.
 *
 * @since 5.0
 */
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" | "url";

/**
 * Supported field formatting types.
 *
 * @since 5.0
 */
export type FieldFormatUnion = DateTimeFieldFormat | NumberFieldFormat;

/**
 * Supported domain types. Domains are used to constrain allowable values of a layer.
 *
 * @since 5.0
 */
export type DomainUnion = RangeDomain | CodedValueDomain | InheritedDomain;

/**
 * Control points used for image-to-map transformations.
 *
 * @since 5.0
 */
export type ControlPoints = [
    ControlPoint,
    ControlPoint
] | [
    ControlPoint,
    ControlPoint,
    ControlPoint
] | [
    ControlPoint,
    ControlPoint,
    ControlPoint,
    ControlPoint
];

export type BasemapGroundLayer = IntegratedMesh3DTilesLayer;

export type GroundLayerUnion = ElevationLayerUnion | BasemapGroundLayer;

/**
 * Supported point label placement types.
 *
 * @since 5.0
 */
export type PointPlacement = "above-center" | "above-left" | "above-right" | "center-center" | "center-left" | "center-right" | "below-center" | "below-left" | "below-right";

/**
 * Supported polyline label placement types.
 *
 * @since 5.0
 */
export type PolylinePlacement = "above-along" | "below-along" | "center-along";

/**
 * Supported polygon label placement types.
 *
 * @since 5.0
 */
export type PolygonPlacement = "always-horizontal";

/**
 * Supported 3D label placement types.
 *
 * @since 5.0
 */
export type PlacementType3D = "above-center" | "above-left" | "above-right" | "center-center" | "center-left" | "center-right" | "below-center" | "below-left" | "below-right";

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

/**
 * Describes attachment-related capabilities for a sublayer.
 *
 * @since 5.0
 */
export interface SublayerCapabilitiesAttachment {
  /**
   * Indicates if resized attachments are supported in the layer.
   * This is useful for showing thumbnails in [Popups](https://developers.arcgis.com/javascript/latest/references/core/widgets/Popup/).
   *
   * @since 5.0
   */
  supportsResize: boolean;
}

/**
 * Describes data-related capabilities for a sublayer.
 *
 * @since 5.0
 */
export interface SublayerCapabilitiesData {
  /**
   * Indicates if attachments are enabled on the layer.
   *
   * @since 5.0
   */
  supportsAttachment: boolean;
}

/**
 * Describes operation-related capabilities for a sublayer.
 *
 * @since 5.0
 */
export interface SublayerCapabilitiesOperations {
  /**
   * Indicates if features in the layer can be [queried](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#queryFeatures).
   *
   * @since 5.0
   */
  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/references/core/layers/FeatureLayer/#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/references/core/rest/support/AttachmentQuery/#objectIds).
   *
   * @since 5.0
   */
  supportsQueryAttachments: boolean;
}

/**
 * Query-related capabilities for a sublayer.
 *
 * @since 5.0
 */
export type SublayerCapabilitiesQueryRelated = Omit<QueryRelatedCapabilities, "supportsCacheHint">;

/**
 * Supported I3S geometry types.
 *
 * @since 5.0
 */
export type I3SGeometryType = "point" | "mesh";

/**
 * The version information for a scene service.
 *
 * @since 5.0
 */
export interface SceneServiceVersion {
  /**
   * The major version of the scene layer.
   *
   * @since 5.0
   */
  major: number;
  /**
   * The minor version of the scene layer.
   *
   * @since 5.0
   */
  minor: number;
  /**
   * The complete version string of the scene layer.
   *
   * @since 5.0
   */
  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 x position of the upper-left corner of the sprite.
   *
   * @since 5.0
   */
  x: number;
  /**
   * The y position of the upper-left corner of the sprite.
   *
   * @since 5.0
   */
  y: number;
  /**
   * The width of the sprite.
   *
   * @since 5.0
   */
  width: number;
  /**
   * The height of the sprite.
   *
   * @since 5.0
   */
  height: number;
  /**
   * The pixel ratio of the sprite.
   *
   * @since 5.0
   */
  pixelRatio?: number;
  /**
   * Whether the sprite is SDF or not.
   *
   * @since 5.0
   */
  sdf?: boolean;
}

/**
 * Image containing sprite data when setting sprite source VectorTileLayer via its [setSpriteSource()](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#setSpriteSource).
 *
 * @see [SpriteSourceImageInfoSpriteSource1x](https://developers.arcgis.com/javascript/latest/references/core/layers/support/types/#SpriteSourceImageInfoSpriteSource1x)
 * @see [SpriteSourceImageInfoSpriteSource2x](https://developers.arcgis.com/javascript/latest/references/core/layers/support/types/#SpriteSourceImageInfoSpriteSource2x)
 * @since 5.0
 */
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/references/core/layers/support/types/#SpriteSourceImageInfo).
 *
 * @since 5.0
 */
export interface ImageObject {
  /**
   * The width of the sprite.
   *
   * @since 5.0
   */
  width: number;
  /**
   * The height of the sprite.
   *
   * @since 5.0
   */
  height: number;
  /**
   * The image data for the sprite.
   *
   * @since 5.0
   */
  data: ArrayBuffer;
}

/**
 * The URL of the sprite source to be fetched when the [VectorTileLayer.setSpriteSource()](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#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 {
  /**
   * For SpriteSourceUrlInfo the type is always "url".
   *
   * @since 5.0
   */
  type: "url";
  /**
   * The sprite source url.
   *
   * @since 5.0
   */
  spriteUrl: string;
  /**
   * The device pixel ratio used to fetch the appropriate sprite source.
   *
   * @since 5.0
   */
  pixelRatio?: number;
  /**
   * The sprite image format.
   *
   * @since 5.0
   */
  spriteFormat?: "png" | "webp";
}

/**
 * Defines sprite source from an image and index json when the [VectorTileLayer.setSpriteSource()](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#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 {
  /**
   * For SpriteSourceImageInfo the type is always "image".
   *
   * @since 5.0
   */
  type: "image";
  /**
   * The sprite source url.
   *
   * @since 5.0
   */
  spriteUrl?: string;
  /**
   * The device pixel ratio of the sprite source.
   *
   * @since 5.0
   */
  pixelRatio?: number;
  /**
   * Sprite source for non high-DPI devices.
   *
   * @since 5.0
   */
  spriteSource1x?: SpriteSourceImageInfoSpriteSource1x;
  /**
   * Sprite source for high-DPI devices.
   *
   * @since 5.0
   */
  spriteSource2x?: SpriteSourceImageInfoSpriteSource2x;
}

/**
 * Image containing sprite data for non high-DPI devices when setting sprite source VectorTileLayer via its [setSpriteSource()](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#setSpriteSource).
 *
 * @since 5.0
 */
export interface SpriteSourceImageInfoSpriteSource1x {
  /**
   * Image containing sprite data for non high-DPI devices.
   *
   * @since 5.0
   */
  image: Image;
  /**
   * The JSON representation of an index file containing a description of each image in the sprite for non high-DPI devices.
   *
   * @since 5.0
   */
  json: Record<string, SpriteInfo | undefined>;
}

/**
 * Image containing sprite data for high-DPI devices when setting sprite source VectorTileLayer via its [setSpriteSource()](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#setSpriteSource).
 *
 * @since 5.0
 */
export interface SpriteSourceImageInfoSpriteSource2x {
  /**
   * Image containing sprite data for high-DPI devices.
   *
   * @since 5.0
   */
  image: Image;
  /**
   * The JSON representation of an index file containing a description of each image in the sprite for high-DPI devices.
   *
   * @since 5.0
   */
  json: Record<string, SpriteInfo | undefined>;
}

/**
 * The sprite source info is used to set the sprites in the VectorTileLayer's [currentStyleInfo.style](https://developers.arcgis.com/javascript/latest/references/core/layers/VectorTileLayer/#currentStyleInfo). The user can set the sprite
 * source from a URL to fetch the sprite resources or from the image info to set the sprite resources directly on the layer.
 *
 * @since 5.0
 */
export type SpriteSourceInfo = SpriteSourceUrlInfo | SpriteSourceImageInfo;

/**
 * The status of the Web Socket connection with the stream service. This property can be watched to see if the connection is lost unexpectedly.
 *
 * @see [StreamConnection.connectionStatus](https://developers.arcgis.com/javascript/latest/references/core/layers/support/StreamConnection/#connectionStatus)
 * @since 5.0
 */
export type StreamConnectionStatus = "disconnected" | "connected";

/**
 * Base editing capability flags.
 *
 * @since 5.0
 */
export interface BaseCapabilities {
  /**
   * Indicates whether a specific operation or capability is enabled on the editable layer.
   *
   * @since 5.0
   */
  enabled: boolean;
}

/**
 * Editing capabilities that account for owner/admin privileges.
 *
 * @since 5.0
 */
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.
   *
   * @since 5.0
   */
  reliesOnOwnerAdminPrivileges: boolean;
}

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

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

/**
 * Specific permissions for delete operations. It takes into account the [Editor's layerInfo](https://developers.arcgis.com/javascript/latest/references/core/widgets/Editor/types/#LayerInfo) settings.
 *
 * @since 5.0
 */
export interface DeleteCapabilities extends OwnerAdminCapabilities {}

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

/**
 * Capabilities for updating attachments.
 *
 * @since 5.0
 */
export interface UpdateAttachmentCapabilities extends AttachmentCapabilities {}

/**
 * Specific permissions for relationship operations. It takes into account the [Editor's layerInfo](https://developers.arcgis.com/javascript/latest/references/core/widgets/Editor/types/#LayerInfo) settings.
 *
 * @since 5.0
 */
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/references/core/rest/support/AttachmentQuery/#returnMetadata) to `true` to get the `exif`
 * info associated with the attachment.
 *
 * @since 5.0
 */
export interface ExifInfo {
  /**
   * The file name.
   *
   * @since 5.0
   */
  name: string;
  /**
   * Array of tag objects containing the following properties:
   *
   * @since 5.0
   */
  tags?: ExifTag[];
}

/**
 * A tag describing an EXIF metadata value.
 *
 * @since 5.0
 */
export interface ExifTag {
  /**
   * The tag name.
   *
   * @since 5.0
   */
  name: string;
  /**
   * The tag description.
   *
   * @since 5.0
   */
  description: string;
  /**
   * The value of the tag.
   *
   * @since 5.0
   */
  value: any;
}

/**
 * Supported Parquet geometry encoding types.
 *
 * @since 5.0
 */
export type ParquetGeometryEncodingUnion = GeometryEncodingWkb | GeometryEncodingLocation;

/**
 * Supported Parquet data source types.
 *
 * @since 5.1
 */
export type ParquetDataUnion = ParquetFilesData | ParquetPortalItemData;

/**
 * Options for creating an elevation sampler.
 *
 * @since 5.0
 */
export interface CreateElevationSamplerOptions extends AbortOptions {
  /**
   * The value that appears in the resulting geometry when there
   *   is no data available.
   *
   * @default 0
   * @since 5.0
   */
  noDataValue?: number;
  /**
   * 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
   * @since 5.0
   */
  demResolution?: ElevationQueryDemResolution;
}

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

/**
 * Resolution options for elevation queries.
 *
 * @since 5.0
 */
export type ElevationQueryDemResolution = number | "finest-contiguous" | "auto";

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

/**
 * Object returned when [ElevationLayer.queryElevation()](https://developers.arcgis.com/javascript/latest/references/core/layers/ElevationLayer/#queryElevation), [Ground.queryElevation()](https://developers.arcgis.com/javascript/latest/references/core/Ground/#queryElevation), or [Ground.queryElevation()](https://developers.arcgis.com/javascript/latest/references/core/Ground/#queryElevation) promise resolves:
 *
 * @since 5.0
 */
export interface ElevationQueryResult<GeometryType> {
  /**
   * The geometry with sampled z-values.
   *
   * @since 5.0
   */
  geometry: GeometryType;
  /**
   * 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.
   *
   * @since 5.0
   */
  sampleInfo?: ElevationQuerySampleInfo[];
  /**
   * The value used when there is no data available.
   *
   * @since 5.0
   */
  noDataValue?: number | null;
}

/**
 * Sampling metadata returned with elevation query results.
 *
 * @since 5.0
 */
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.
   *
   * @since 5.0
   */
  demResolution: number;
  /**
   * The elevation source from which
   *                                   the data for the corresponding coordinate was sampled.
   *
   * @since 5.0
   */
  source?: GroundLayerUnion;
}

/**
 * Describes elevation contained in the pixels that comprise an elevation tile.
 *
 * @since 5.0
 */
export interface ElevationTileData {
  /**
   * The elevation values of each pixel in the tile.
   *
   * @since 5.0
   */
  readonly values: ArrayLike<number>;
  /**
   * The width of the tile in pixels.
   *
   * @since 5.0
   */
  readonly width: number;
  /**
   * The height of the tile in pixels.
   *
   * @since 5.0
   */
  readonly height: number;
  /**
   * Indicating the pixel values where no elevation data is present.
   *
   * @since 5.0
   */
  readonly noDataValue: number;
}