import type Extent from "./Extent.js";
import type BaseGeometry from "./Geometry.js";
import type Point from "./Point.js";
import type { Curve as CurveJSON } from "../portal/jsonTypes.js";
import type { GeometryProperties as BaseGeometryProperties } from "./Geometry.js";

export interface PolygonProperties extends BaseGeometryProperties, Partial<Pick<Polygon, "curveRings" | "rings">> {}

/**
 * A polygon contains an array of [rings](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#rings) and a [spatialReference](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#spatialReference).
 * Each ring is represented as an array of points. The first and last points of a ring must be the same.
 * A polygon also has boolean-valued [hasM](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasM) and [hasZ](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasZ) fields.
 *
 * > [!WARNING]
 * >
 * > **Known Limitations**
 * >
 * > The polygon geometries must be simple when added to the following layers:
 * >  * [View.graphics](https://developers.arcgis.com/javascript/latest/references/core/views/View/#graphics)
 * >  * [GraphicsLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/GraphicsLayer/)
 * >  * Feature collections added through [FeatureLayer.source](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#source)
 * >
 * > Polygon geometries can be inspected and simplified before they are added to those layers.
 * > Use the [simplifyOperator](https://developers.arcgis.com/javascript/latest/references/core/geometry/operators/simplifyOperator/)
 * > to make sure that polygons display correctly on the client-side.
 *
 * @since 4.0
 * @see [Sample - Add graphics (MapView)](https://developers.arcgis.com/javascript/latest/sample-code/intro-graphics/)
 * @see [Sample - Add graphics (SceneView)](https://developers.arcgis.com/javascript/latest/sample-code/graphics-basic-3d/)
 */
export default class Polygon extends BaseGeometry {
  /**
   * Converts the given Extent to a Polygon instance. This is useful for scenarios in which
   * you would like to display an area of interest, which is typically defined by an Extent or bounding box,
   * as a polygon with a fill symbol in the view. Some geoprocessing tools require input
   * geometries to be of a Polygon type and not an Extent.
   *
   * @param extent - An extent object to convert to a polygon.
   * @returns A polygon instance representing the given extent.
   * @example
   * view.on("click", function(evt) {
   *   const area = Polygon.fromExtent(view.extent);
   *   const graphic = new Graphic({
   *     geometry: area,
   *     symbol: { type: "simple-fill" }
   *   });
   *   view.graphics.add(graphic);
   * });
   */
  static fromExtent(extent: Extent): Polygon;
  constructor(properties?: PolygonProperties);
  /**
   * The centroid of the polygon. For a polygon with multiple rings, it represents the centroid of the largest ring.
   *
   * @deprecated since version 4.34. Please use the [centroidOperator](https://developers.arcgis.com/javascript/latest/references/core/geometry/operators/centroidOperator/) instead.
   */
  get centroid(): Point | null | undefined;
  /**
   * An array consisting of points and [curve objects](https://developers.arcgis.com/rest/services-reference/enterprise/geometry-objects/#curve-objects) that define the polygon geometry.
   *
   * @example
   * // Polygon with a circular arc segment represented with a curve object.
   * const curvedPolygon = new Polygon({
   *   curveRings: [
   *     [
   *       [-14008897.772168774,4219321.966491825],
   *       {
   *         c: [
   *           [-13922799.120303603,4218343.568797498],
   *           [-13964136.273597848,4258702.324397415]
   *         ]
   *       },
   *       [-13922799.120303603,4218343.568797498],
   *       [-13923532.913909001,4170157.6736310786],
   *       [-14010365.378040956,4170402.2683893144],
   *       [-14008897.772168774,4219321.966491825]
   *     ]
   *   ],
   *   spatialReference: { wkid: 102100 }
   * });
   */
  accessor curveRings: CurveJSON[][] | undefined;
  /**
   * An array of rings. Each ring is a two-dimensional array of numbers representing the coordinates of
   * each vertex in the ring in the spatial reference of the view. The first vertex of each ring should always be
   * the same as the last vertex. Each vertex is an array of two, three, or four numbers.
   * The table below shows the various structures of a vertex array.
   * Case | Vertex array
   * --- | ---
   * [without z](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasZ) and [without m](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasM)| [x, y]
   * [without z](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasZ) and [with m](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasM) | [x, y, m]
   * [with z](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasZ) and [without m](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasM) | [x, y, z]
   * [with z](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasZ) and [with m](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#hasM) | [x, y, z, m]
   *
   * @example
   * //3D polygon rings with m-values (note that the second ring does not have m-values defined for it)
   * const rings = [
   *   [  // first ring
   *     [-97.06138,32.837,35.1,4.8],
   *     [-97.06133,32.836,35.2,4.1],
   *     [-97.06124,32.834,35.3,4.2],
   *     [-97.06138,32.837,35.1,4.8]  // same as first vertex
   *   ], [  // second ring
   *     [-97.06326,32.759,35.4],
   *     [-97.06298,32.755,35.5],
   *     [-97.06153,32.749,35.6],
   *     [-97.06326,32.759,35.4]  // same as first vertex
   *  ]
   * ];
   *
   * const polygon = new Polygon({
   *   hasZ: true,
   *   hasM: true,
   *   rings: rings,
   *   spatialReference: { wkid: 4326 }
   * });
   */
  accessor rings: number[][][];
  /** The string value representing the type of geometry. */
  get type(): "polygon";
  /**
   * Adds a ring to the Polygon. The ring can be one of the following: an array of numbers or an array of points.
   * When added the index of the ring is incremented by one.
   *
   * @param points - A polygon ring. The first and last coordinates/points
   *                                                           in the ring must be the same. This can either be defined as an array of
   *                                                           Point geometries or an array of XY coordinates.
   * @returns Returns the polygon with the new ring included.
   */
  addRing(points: number[][] | Point[]): this;
  /**
   * Creates a deep clone of Polygon object.
   *
   * @returns A new instance of a Polygon object equal to the object used to call `.clone()`.
   */
  clone(): Polygon;
  /**
   * Checks on the client if the input point is inside the polygon. A point on the polygon line is considered inside.
   *
   * @param point - The point to test whether it is contained within the testing polygon.
   * @returns Returns `true` if the point is located inside the polygon.
   */
  contains(point: Point): boolean;
  /**
   * Returns a point specified by a ring and point in the path.
   *
   * @param ringIndex - The index of the ring containing the desired point.
   * @param pointIndex - The index of the desired point within the ring.
   * @returns Returns the point at the specified ring index and point index.
   */
  getPoint(ringIndex: number, pointIndex: number): Point | null | undefined;
  /**
   * Inserts a new point into the polygon.
   *
   * @param ringIndex - The index of the ring in which to insert the point.
   * @param pointIndex - The index of the point to insert within the ring.
   * @param point - The point to insert.
   * @returns Returns the updated polygon.
   */
  insertPoint(ringIndex: number, pointIndex: number, point: Point | number[]): this;
  /**
   * Checks if a Polygon ring is clockwise.
   *
   * @param ring - A polygon ring. It can either be defined as an array of
   *    Point geometries or an array of XY coordinates.
   * @returns Returns `true` if the ring is clockwise and `false` for counterclockwise.
   */
  isClockwise(ring: number[][] | Point[]): boolean;
  /**
   * Removes a point from the polygon at the given `pointIndex` within the ring identified by `ringIndex`.
   *
   * @param ringIndex - The index of the ring containing the point to remove.
   * @param pointIndex - The index of the point to remove within the ring.
   * @returns Returns the geometry of the removed point.
   */
  removePoint(ringIndex: number, pointIndex: number): Point | null | undefined;
  /**
   * Removes a ring from the Polygon. The index specifies which ring to remove.
   *
   * @param index - The index of the ring to remove.
   * @returns Returns array of points representing the removed ring.
   */
  removeRing(index: number): Point[] | null | undefined;
  /**
   * Updates a point in the polygon.
   *
   * @param ringIndex - The index of the ring containing the point to update.
   * @param pointIndex - The index of the point to update within the ring.
   * @param point - The new point geometry.
   * @returns Returns the updated polygon.
   */
  setPoint(ringIndex: number, pointIndex: number, point: Point | number[]): this;
}