/**
 * A utility method for getting a Reshape instance with support for connected reshaping.
 *
 * @experimental
 * @internal
 */
import type Graphic from "../../Graphic.js";
import type Point from "../../geometry/Point.js";
import type FeatureLayer from "../../layers/FeatureLayer.js";
import type GeoJSONLayer from "../../layers/GeoJSONLayer.js";
import type GraphicsLayer from "../../layers/GraphicsLayer.js";
import type OrientedImageryLayer from "../../layers/OrientedImageryLayer.js";
import type SceneLayer from "../../layers/SceneLayer.js";
import type KnowledgeGraphSublayer from "../../layers/knowledgeGraph/KnowledgeGraphSublayer.js";
import type SubtypeSublayer from "../../layers/support/SubtypeSublayer.js";
import type MapView from "../../views/MapView.js";
import type LinkChartView from "../../views/LinkChartView.js";
import type { ResourceHandle } from "../../core/Handles.js";

/**
 * @experimental
 * @internal
 */
export interface ReshapeInterface {
  /**
   * An array of `ConnectedReshapeProviderFactory` objects that provide connected reshape functionality for the reshape operation.
   *
   * @experimental
   */
  connectedReshapeProviders?: ConnectedReshapeProviderFactory[] | null;
  /**
   * The graphic being reshaped.
   *
   * @experimental
   */
  graphic: Graphic;
  /**
   * A GraphicsLayer to be used for temporary graphics during the reshape operation.
   *
   * @experimental
   */
  layer: GraphicsLayer;
  /**
   * The MapView in which the reshape operation is taking place.
   *
   * @experimental
   */
  view: MapView;
  /**
   * Destroys the reshape instance.
   *
   * @experimental
   */
  destroy: () => void;
}

/**
 * Returns a reshape operation.
 *
 * @param properties - The constructor properties.
 * @experimental
 * @internal
 */
export function getReshape(properties: ReshapeProperties): Promise<ReshapeInterface>;

/**
 * @experimental
 * @internal
 */
export type ReshapeProperties = Pick<ReshapeInterface, "connectedReshapeProviders" | "graphic" | "layer" | "view">;

/**
 * Starts a node movement reshape operation in the LinkChartView.
 *
 * @param targetEntityId - The ID of the entity to be moved.
 * @param targetEntityType - The type of the entity to be moved.
 * @param targetView - The LinkChartView in which the node movement reshape operation will take place.
 * @param options - Optional parameters.
 * @experimental
 * @internal
 */
export function studioStartNodeMovement(targetEntityId: string, targetEntityType: string, targetView: LinkChartView, options?: { useSnappingGrid?: boolean; }): Promise<StudioNodeMovementReturnType>;

/**
 * @experimental
 * @internal
 */
export interface StudioNodeMovementReturnType {
  /** @experimental */
  commitNodeMovement: () => Promise<Point>;
  /** @experimental */
  cancelNodeMovement: () => void;
}

/**
 * Registers a callback to be called when the node movement reshape operation stops.
 *
 * @param targetView - The LinkChartView in which the node movement reshape operation is taking place.
 * @param movementStopCallback - The callback function to be called when the node movement stops.
 * @experimental
 * @internal
 */
export function onNodeMovementStop(targetView: LinkChartView, movementStopCallback: MovementStopCallback): ResourceHandle;

/**
 * @param event - The event object containing information about the node movement.
 * @experimental
 * @internal
 */
export type MovementStopCallback = (event: MovementStopCallbackEvent) => void;

/**
 * @experimental
 * @internal
 */
export interface MovementStopCallbackEvent {
  /**
   * The graphic representing the node being moved.
   *
   * @experimental
   */
  mover: Graphic;
  /**
   * The change in the x-coordinate of the node's position.
   *
   * @experimental
   */
  dx: number;
  /**
   * The change in the y-coordinate of the node's position.
   *
   * @experimental
   */
  dy: number;
}

/**
 * @experimental
 * @internal
 */
export type ConnectedReshapeSupportedLayer = GeoJSONLayer | FeatureLayer | OrientedImageryLayer | SceneLayer | SubtypeSublayer | GraphicsLayer | KnowledgeGraphSublayer;

/**
 * @experimental
 * @internal
 */
export interface ReshapeFeature {
  /**
   * The graphic being reshaped.
   *
   * @experimental
   */
  graphic: Graphic;
  /**
   * The layer to which the graphic belongs.
   *
   * @experimental
   */
  layer: ConnectedReshapeSupportedLayer;
}

/**
 * Represents a feature that has been modified by a ConnectedReshapeProvider due to being connected (or otherwise spatially related to) the feature(s) or vertex(es) being reshaped.
 *
 * @experimental
 * @internal
 */
export interface ModifiedFeature {
  /**
   * The current version of the feature, with a geometry that reflects whatever translation/transformation has been made as a result of the reshape operation
   *
   * @experimental
   */
  graphic: Graphic;
  /**
   * The layer to which the feature belongs
   *
   * @experimental
   */
  layer: ConnectedReshapeSupportedLayer;
  /**
   * The original version of the feature, before the start of the reshape operation
   *
   * @experimental
   */
  originalGraphic: Graphic;
  /**
   * Some unique identifier of the feature. E.g., the feature's object ID.
   *
   * @experimental
   */
  uniqueId: string | number;
}

/**
 * An object that provides "connected reshaping" functionality for a feature or vertex being reshaped. This object is given information about each incremental position change throughout the reshape operation. The object is responsible for identifying and loading features that are connected to the primary feature, and providing, for each incremental change, the current positions of those connected features.
 *
 * @experimental
 * @internal
 */
export interface ConnectedReshapeProvider {
  /**
   * Not used at this time. Hard-code this to `false` for now.
   *
   * @experimental
   */
  updating: boolean;
  /**
   * Called each time the position of the given feature or vertex changes during the reshape operation. This function should return an array of [ModifiedFeature](https://developers.arcgis.com/javascript/latest/api-reference/esri-applications-KnowledgeStudio-reshape.html#ModifiedFeature) objects representing the current positions of the connected features given the change so far to the position of the feature or vertex being reshaped. Parameters represent the incremental change in position of the feature (or vertex) since the last time `translate` was called. Values are in the units of the spatial reference.
   *
   * @experimental
   */
  translate: TranslateMethod;
}

/**
 * A function that is called each time the position of the feature or vertex being reshaped changes during the reshape operation. For example, when a user is dragging the feature/vertex, this function will be called repeatedly with the incremental position changes. In other words, it is called roughly once per "pointer-move" event.
 *
 * @param deltaX - The change in the x-coordinate of the feature or vertex being reshaped, in the units of the spatial reference.
 * @param deltaY - The change in the y-coordinate of the feature or vertex being reshaped, in the units of the spatial reference.
 * @param deltaZ - The change in the z-coordinate of the feature or vertex being reshaped, in the units of the spatial reference. This may be `nil` if the feature does not have a z-coordinate.
 * @returns An array of modified features representing the current positions of the connected features given the change so far to the position of the feature or vertex being reshaped.
 * @experimental
 * @internal
 */
export type TranslateMethod = (deltaX: number, deltaY: number, deltaZ: number | null | undefined) => ModifiedFeature[];

/**
 * @experimental
 * @internal
 */
export interface VertexInfo {
  /**
   * The index of the ring or path in the feature's geometry that contains the vertex.
   *
   * @experimental
   */
  pathIndex: number;
  /**
   * The index of the vertex in the ring or path.
   *
   * @experimental
   */
  vertexIndex: number;
}

/**
 * @experimental
 * @internal
 */
export interface ConnectedReshapeProviderFactory {
  /**
   * Called at the start a reshape operation involving one or more whole features. Should return a `ConnectedReshapeProvider` that will provide information about features connected to this feature(s) during the reshape operation.
   *
   * @experimental
   */
  getFeatureReshapeProvider: GetFeatureReshapeProvider;
  /**
   * Called at the start of a reshape operation involving one or more vertices of a feature. Should return a `ConnectedReshapeProvider` that will provide information about features connected to this vertex(es) during the reshape operation.
   *
   * @experimental
   */
  getVertexReshapeProvider: GetVertexReshapeProvider;
}

/**
 * @param features - The features being reshaped.
 * @returns The connected reshape provider for the features.
 * @experimental
 * @internal
 */
export type GetFeatureReshapeProvider = (features: ReshapeFeature[]) => ConnectedReshapeProvider | null | undefined;

/**
 * @param feature - The feature being reshaped.
 * @param vertices - The vertices of the feature being reshaped.
 * @returns The connected reshape provider for the feature.
 * @experimental
 * @internal
 */
export type GetVertexReshapeProvider = (feature: ReshapeFeature, vertices: VertexInfo[]) => ConnectedReshapeProvider | null | undefined;