import type Point from "../../Point.js";
import type MeshMaterial from "../MeshMaterial.js";
import type { AbortOptions } from "../../../core/promiseUtils.js";
import type { LengthUnit } from "../../../core/units.js";
import type { MeshMaterialProperties } from "../MeshMaterial.js";
import type { ElevationQueryDemResolution } from "../../../layers/support/types.js";

export interface VertexSpaceParameters {
  /** The unit of the size (defaults to the unit of the location's spatial reference). */
  unit?: LengthUnit;
  /**
   * Specifies the vertex space of the created mesh. If not specified, a vertex space is
   *   chosen according to the spatial reference where a [MeshLocalVertexSpace](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-support-MeshLocalVertexSpace.html) is used for geographic
   *   or WebMercator spatial references and [MeshGeoreferencedVertexSpace](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-support-MeshGeoreferencedVertexSpace.html) is used otherwise.
   *   If vertexSpace is "georeferenced", then the vertex space will be also [MeshGeoreferencedVertexSpace](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-support-MeshGeoreferencedVertexSpace.html)
   *   where mesh vertices are georeferenced offsets to the location provided with the create function. If the vertexSpace is "georeferenced" and the spatial reference is geographic, then vertex data will be first created in a "local" vertex space and then converted to the "georeferenced" vertex space. In case of vertex space being "local", mesh vertices
   *   will be defined in a local cartesian coordinate frame placed at the location provided with the create function. This will be
   *   represented by [MeshLocalVertexSpace](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-support-MeshLocalVertexSpace.html)
   */
  vertexSpace?: "local" | "georeferenced";
}

export interface GeographicParameters {
  /**
   * Whether to georeference relative to the globe or the
   *   projected coordinate system (PCS). This parameter is only relevant for spatial references that can
   *   be used in both local and global viewing modes (currently only WebMercator), and otherwise ignored.
   *   The default value is true. When true, the transformation is applied in a Cartesian system with respect to the local
   *   coordinate system on the globe.
   *
   * @deprecated Since version 4.30. Use mesh `vertexSpace` and spatial reference to control how operations are performed instead.
   */
  geographic?: boolean;
  /**
   * Indicates the unit of the source data. A linear scale will be applied to the position attributes
   *   to convert the source data to the unit of the spatial reference at which the mesh is being georeferenced. By default the unit
   *   of the source data is assumed to be the same as the target spatial reference.
   */
  unit?: LengthUnit;
}

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

export interface CreateParameters extends VertexSpaceParameters, MaterialParameters {
  /**
   * A uniform size value or an object containing individual
   *   values width, height and depth. The unit of the size values is derived from the spatial reference
   *   of the provided location, unless a specific unit is specified. The default unit for a GCS is always meters.
   */
  size?: number | Size;
}

/** Parameters used to configure box mesh creation. */
export interface CreateBoxParameters extends CreateParameters {
  /**
   * The face for generating image uv coordinates. By default, a single set of unwrapped
   *   UV coordinates are generated for all the faces. By setting the `imageFace` parameter to one of `east`, `west`,
   *   `north`, `south`, `up` or `down`, the specified face will have full sized UV coordinates while the other faces
   *   will pertain their regular unwrapped UV coordinates. This is useful for applying an image only to a single face of the box.
   *   The provided `material` parameter will be applied to the specified `imageFace`. The resulting mesh will have two components,
   *   the first contains the selected image face and the second contains the other faces of the box.
   */
  imageFace?: Facing | "all";
}

/** Parameters used to configure sphere mesh creation. */
export interface CreateSphereParameters extends CreateParameters {
  /**
   * The additional number of subdivisions for generating
   *   the mesh representing a sphere. A densificationFactor parameter of 0 will generate a default of
   *   16-by-16 vertices to approximate the sphere. A densificationFactor of 1 will generate 32-by-32 vertices, etc.
   *   The larger the densificationFactor, the better the mesh will approximate a perfect sphere (at the cost of
   *   processing and rendering performance).
   */
  densificationFactor?: number;
}

/** Parameters used to configure cylinder mesh creation. */
export interface CreateCylinderParameters extends CreateParameters {
  /**
   * The additional number of subdivisions for generating
   *   the mesh representing a sphere. A densificationFactor parameter of 0 will generate a default of
   *   16-by-16 vertices to approximate the sphere. A densificationFactor of 1 will generate 32-by-32 vertices, etc.
   *   The larger the densificationFactor, the better the mesh will approximate a perfect sphere (at the cost of
   *   processing and rendering performance).
   */
  densificationFactor?: number;
}

/** Parameters used to configure plane mesh creation. */
export interface CreatePlaneParameters extends VertexSpaceParameters, MaterialParameters {
  /**
   * Direction the plane is facing.
   *
   * @default "up"
   */
  facing?: Facing;
  /**
   * A uniform size value or an object containing individual
   *   values width and height. The unit of the size values is derived from the spatial reference
   *   of the provided location, unless a specific unit is specified. The default unit for a GCS is always meters.
   */
  size?: number | Size2D;
}

/** Parameters used to configure gltf mesh creation. */
export interface CreateFromGLTFParameters extends VertexSpaceParameters, AbortOptions {}

export interface RotateParameters extends GeographicParameters {
  /**
   * The origin around which to rotate. If not specified,
   *   the mesh will be rotated around the {@link geometry/support/meshUtils/interfaces#origin Mesh.origin}.
   */
  origin?: Point;
}

export interface ScaleParameters extends GeographicParameters {
  /**
   * The origin point for scaling. If not specified, the mesh will
   *   be scaled around the {@link geometry/support/meshUtils/interfaces#origin Mesh.origin}.
   */
  origin?: Point;
}

export interface CenterAtParameters extends GeographicParameters {
  /**
   * The origin at which to center. If not specified,
   *   the mesh will be centered at the mesh extent center.
   */
  origin?: Point;
}

export interface CreateFromElevationParameters extends AbortOptions {
  /**
   * Controls the horizontal resolution (cell size)
   *   in meters from which elevation data is sampled (defaults to `auto`). See [ElevationLayer#queryElevation()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ElevationLayer.html#queryElevation)
   *   for more details on the different settings.
   *
   * @default "auto"
   */
  demResolution?: ElevationQueryDemResolution;
  /** The material to be used for the mesh. */
  material?: MeshMaterial;
}

export interface CreateElevationSamplerParameters {
  /**
   * The value to use when there is no data available.
   *
   * @default 0
   */
  noDataValue?: number;
}

export interface Size {
  /** The depth of the created mesh. */
  depth?: number;
  /** The height of the created mesh. */
  height?: number;
  /** The width of the created mesh. */
  width?: number;
}

export interface Size2D {
  /** The height of the created mesh. */
  height?: number;
  /** The width of the created mesh. */
  width?: number;
}

export type Facing = "east" | "west" | "north" | "south" | "up" | "down";

export interface ExportGLTFOptions extends AbortOptions {
  /**
   * The origin of the exported model,
   *   defaults to {@link geometry/support/meshUtils/exporters/gltf/interfaces#origin Mesh.origin}. This location will correspond to the (0, 0, 0) coordinate
   *   in the binary glTF model.
   */
  origin: Point;
}

/** Represents the position and normal vertex attribute buffers of a mesh. */
export interface VertexAttributes {
  /** The normal buffer. */
  normal?: Float32Array<ArrayBuffer> | null;
  /** The position buffer. */
  position: Float64Array<ArrayBuffer>;
  /** The tangent buffer. */
  tangent?: Float32Array<ArrayBuffer> | null;
}