/**
 * Various utilities and convenience functions for working with [Mesh](https://developers.arcgis.com/javascript/latest/references/core/geometry/Mesh/) objects.
 *
 * @since 4.7
 */
import type Ground from "../../Ground.js";
import type Extent from "../Extent.js";
import type Mesh from "../Mesh.js";
import type Point from "../Point.js";
import type Polygon from "../Polygon.js";
import type ElevationLayer from "../../layers/ElevationLayer.js";
import type ElevationSampler from "../../layers/support/ElevationSampler.js";
import type { AbortOptions } from "../../core/promiseUtils.js";
import type { MeshVertexSpaceUnion } from "../types.js";
import type { MeshMaterialProperties } from "./MeshMaterial.js";
import type { CreateBoxParameters, CreateCylinderParameters, CreateElevationSamplerParameters, CreateFromElevationParameters, CreateFromGLTFParameters, CreatePlaneParameters, CreateSphereParameters } from "./meshUtils/types.js";

/**
 * Creates a mesh geometry by sampling elevation data from an elevation service on a regular grid.
 *
 * @param source - The source from which to query the elevation data.
 * @param extent - The extent from which to create the mesh.
 * @param options - Additional options.
 * @returns A promise that resolves to a mesh geometry representing the elevation data at the specified extent.
 * @example
 * // Create a mesh by sampling the ground
 * meshUtils.createFromElevation(map.ground, extent)
 *     .then(function(mesh) {
 *       // Do something with the mesh
 *     });
 *
 * // Create a mesh by sampling the ground surface currently in view
 * meshUtils.createFromElevation(view.groundView.elevationSampler, view.extent)
 *     .then(function(mesh) {
 *       // Do something with the mesh
 *     });
 */
export function createFromElevation(source: ElevationLayer | Ground | ElevationSampler, extent: Extent, options?: CreateFromElevationParameters): Promise<Mesh>;

/**
 * Creates an elevation sampler from a mesh. The sampler can be used to query elevation values
 * on the surface of the mesh. The elevation sampler uses a snapshot of the Mesh geometry and
 * will not update if the mesh changes after the sampler has been created.
 *
 * @param mesh - The mesh geometry used to create the elevation sampler.
 * @param options - Additional options.
 * @returns An elevation sampler.
 * @since 4.12
 * @see [Sample - Low poly terrain using mesh geometry](https://developers.arcgis.com/javascript/latest/sample-code/geometry-mesh-elevation/)
 */
export function createElevationSampler(mesh: Mesh, options?: CreateElevationSamplerParameters): Promise<ElevationSampler>;

/**
 * Merges multiple meshes into a single mesh. All mesh geometries
 * must be in the same spatial reference.
 *
 * @param geometries - One or more meshes.
 * @returns The merged mesh geometry.
 * @example const mergedMesh = meshUtils.merge([mesh1, mesh2]);
 */
export function merge(geometries: Mesh[]): Mesh | null | undefined;

/**
 * Converts a mesh to a new vertex space. Any [transform](https://developers.arcgis.com/javascript/latest/references/core/geometry/support/MeshTransform/) defined on
 * the mesh will be applied to the vertex attributes as part of the conversion.
 *
 * @param mesh - the mesh geometry to convert.
 * @param targetVertexSpace - the vertex space to convert to.
 * @param options - Additional options.
 * @returns The converted mesh.
 * @since 4.30
 * @example
 * // convert cartesian model data to a mesh with absolute coordinates in WebMercator
 * const converted = await convertVertexSpace(
 *   new Mesh({
 *     vertexSpace: new MeshLocalVertexSpace({ origin: location }),
 *     vertexAttributes,
 *     spatialReference: SpatialReference.WebMercator
 *   }),
 *   new MeshGeoreferencedVertexSpace()
 * );
 * @example
 * // convert a georeferenced WebMercator mesh to a mesh with a local tangent coordinate frame
 * // at the center of the mesh
 * const center = mesh.extent.center;
 * const origin = [center.x, center.y, center.z];
 * const converted = await convertVertexSpace(mesh, new MeshLocalVertexSpace({ origin }));
 * @example
 * // convert an existing mesh to absolute coordinates and place a graphic on the highest vertex
 * const converted = await convertVertexSpace(mesh, new MeshGeoreferencedVertexSpace());
 * const position = converted.vertexAttributes.position;
 *
 * const highestPoint = new Point({ x: 0, y: 0, z: 0, spatialReference: converted.spatialReference });
 *
 * for (let i = 0; i < position.length; i += 3) {
 *   if (position[i + 2] > highestPoint.z) {
 *     highestPoint.x = position[i];
 *     highestPoint.y = position[i + 1];
 *     highestPoint.z = position[i + 2];
 *   }
 * }
 *
 * view.graphics.add(new Graphic({ geometry: highestPoint }));
 */
export function convertVertexSpace(mesh: Mesh, targetVertexSpace: MeshVertexSpaceUnion, options?: AbortOptions): Promise<Mesh>;

/**
 * Creates a mesh representing a box. The spatial reference of the resulting mesh is the same
 * as the location where it is placed.
 *
 * #### Box UV coordinate space
 *
 * The box geometry will have UV coordinates generated according to the following scheme:
 *
 * <img src="https://developers.arcgis.com/javascript/latest/assets/references/core/geometry/mesh-box-uv.avif" width="300" height="300"/>
 *
 * @param location - The location bottom center of the box.
 * @param parameters - Parameters to configure the box creation.
 * @returns The resulting mesh.
 * @since 5.1
 * @example
 * let mesh = meshUtils.createBox(point, {
 *   size: {
 *     width: 10,
 *     height: 100,
 *     depth: 20
 *   },
 *   material: {
 *     color: "green"
 *   }
 * });
 * @example
 * let mesh = meshUtils.createBox(point, {
 *   imageFace: "top",
 *   material: {
 *     colorTexture: new MeshTexture({ url: "./url-to-image.png" })
 *   }
 * });
 */
export function createBox(location: Point, parameters?: CreateBoxParameters): Mesh;

/**
 * Creates a mesh representing a sphere. The spatial reference of the resulting mesh is the same
 * as the location where it is placed.
 *
 * #### Sphere UV coordinate space
 *
 * The sphere geometry will have UV coordinates generated according to the following scheme
 * (example is shown for 8x8 vertices sphere):
 *
 * <img src="https://developers.arcgis.com/javascript/latest/assets/references/core/geometry/mesh-sphere-uv.avif" width="300" height="300"/>
 *
 * @param location - The location of the bottom center of the sphere.
 * @param parameters - Parameters to configure the sphere creation.
 * @returns The resulting mesh.
 * @since 5.1
 */
export function createSphere(location: Point, parameters?: CreateSphereParameters): Mesh;

/**
 * Creates a mesh representing a cylinder. The spatial reference of the resulting mesh is the same
 * as the location where it is placed.
 *
 * #### Cylinder UV coordinate space
 *
 * The cylinder geometry will have UV coordinates generated according to the following scheme
 * (example is shown for 8 vertices cylinder):
 *
 * <img src="https://developers.arcgis.com/javascript/latest/assets/references/core/geometry/mesh-cylinder-uv.avif" width="300" height="300"/>
 *
 * @param location - The location of the bottom center of the cylinder.
 * @param parameters - Parameters to configure the cylinder creation.
 * @returns The resulting mesh.
 * @since 5.1
 */
export function createCylinder(location: Point, parameters?: CreateCylinderParameters): Mesh;

/**
 * Creates a mesh representing a plane. The spatial reference of the resulting mesh is the same
 * as the location where it is placed. A plane consists of two triangles and may be conveniently
 * oriented at creation time.
 *
 * #### Plane UV coordinate space
 *
 * The plane geometry will have UV coordinates generated according to the following scheme:
 *
 * <img src="https://developers.arcgis.com/javascript/latest/assets/references/core/geometry/mesh-plane-uv.avif" width="300" height="300"/>
 *
 * @param location - The location of the bottom center of the plane.
 * @param parameters - Parameters to configure the plane creation.
 * @returns The resulting mesh.
 * @since 5.1
 */
export function createPlane(location: Point, parameters?: CreatePlaneParameters): Mesh;

/**
 * Creates a new mesh geometry from a polygon geometry. The resulting mesh contains only
 * a position vertex attribute and a single component with faces. The default shading will be
 * set to `flat`. The spatial reference of the resulting mesh is the same
 * as the input polygon. The resulting mesh will not contain any UV nor normal vertex attributes.
 *
 * @param polygon - The input polygon.
 * @param parameters - Optional parameters.
 * @returns A promise that resolves to a new mesh representing the triangulated polygon.
 * @since 5.1
 */
export function createFromPolygon(polygon: Polygon, parameters?: CreateFromPolygonParameters): Promise<Mesh>;

/**
 * Creates a new mesh geometry from a glTF model referenced by the `url` parameter.
 *
 * The spatial reference of the resulting mesh is the same as the spatial reference of the `location` parameter. For
 * more information on the supported glTF features you can read the
 * [Visualizing points with 3D symbols](https://developers.arcgis.com/javascript/latest/visualizing-points-3d/) guide topic. Animations are currently unsupported.
 *
 * @param location - The location of the origin of the model. If the location does not
 *  contain a z-value, z is assumed to be `0`.
 * @param url - The URL of the glTF model. The URL should point to a glTF file (.gltf or .glb) which can
 *  reference additional binary (.bin) and image files (.jpg, .png).
 * @param parameters - Parameters to configure the mesh from glTF creation.
 * @returns A promise that resolves to a mesh geometry representing the loaded
 *  glTF model.
 * @since 5.1
 */
export function createFromGLTF(location: Point, url: string, parameters?: CreateFromGLTFParameters): Promise<Mesh>;

export interface CreateFromPolygonParameters {
  /** The material to be used for the mesh. */
  material?: MeshMaterialProperties;
}