import type LineOfSightAnalysis from "../../analysis/LineOfSightAnalysis.js";
import type Accessor from "../../core/Accessor.js";
import type Collection from "../../core/Collection.js";
import type Point from "../../geometry/Point.js";
import type SceneView from "../../views/SceneView.js";
import type LineOfSightTarget from "./LineOfSightTarget.js";
import type { LineOfSightAnalysisProperties } from "../../analysis/LineOfSightAnalysis.js";
import type { PointProperties } from "../../geometry/Point.js";
import type { LineOfSightTargetProperties } from "./LineOfSightTarget.js";
import type { ReadonlyArrayOrCollection } from "../../core/Collection.js";

/** @deprecated since version 4.33. Use the [LineOfSightAnalysis](https://developers.arcgis.com/javascript/latest/references/core/analysis/LineOfSightAnalysis/) or [Line Of Sight component](https://developers.arcgis.com/javascript/latest/references/map-components/components/arcgis-line-of-sight/) instead. For information on widget deprecation, read about [Esri's move to web components](https://developers.arcgis.com/javascript/latest/components-transition-plan/). */
export interface LineOfSightViewModelProperties extends Partial<Pick<LineOfSightViewModel, "view">> {
  /**
   * The line of sight analysis object being created or modified by the view model.
   *
   * If no analysis is provided, the view model automatically creates its own analysis and adds it to the view. In
   * this case, the analysis will also be automatically removed from the view when the view model is destroyed.
   *
   * @since 4.23
   * @example
   * // Construct a line of sight analysis object outside of the view model
   * const analysis = new LineOfSightAnalysis({
   *   observer: {
   *     type: "point", // autocasts as new Point()
   *     x: 7.67,
   *     y: 45.981,
   *     z: 3435.765
   *   },
   *   targets: [{
   *     location: {
   *       type: "point",
   *       x: 7.659,
   *       y: 45.976,
   *       z: 4437
   *     }
   *   }]
   * });
   *
   * // Ensure that the analysis is added to the view
   * view.analyses.add(analysis);
   *
   * // Frame the analysis in the view
   * view.goTo(analysis.extent);
   *
   * // Pass the analysis object as a constructor parameter to modify it using the view model
   * const viewModel = new LineOfSightViewModel({
   *   analysis: analysis,
   *   view: view
   * });
   */
  analysis?: LineOfSightAnalysisProperties;
  /**
   * The observer's viewpoint from which lines of sight will be drawn towards the targets. The Z
   * value of the point is an absolute value.
   */
  observer?: PointProperties | null;
  /**
   * A collection of [LineOfSightTarget](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightTarget/) containing
   * the target location and the analysis results.
   */
  targets?: ReadonlyArrayOrCollection<LineOfSightTargetProperties>;
}

/**
 * Provides the logic for the [LineOfSight](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/) widget and [component](https://developers.arcgis.com/javascript/latest/references/map-components/components/arcgis-line-of-sight/). Use it to create
 * your own widget or to get access to the observer, targets and intersections.
 *
 * Using the LineOfSightViewModel you can programmatically set the observer and targets for
 * a line of sight analysis.
 *
 * ```js
 * // instantiate a new view model
 * const losViewModel = new LineOfSightViewModel({
 *  view: view
 * });
 *
 * // set an observer
 * losViewModel.observer = new Point({
 *   latitude: 42.3521,
 *   longitude: -71.0559,
 *   z: 147.139
 * });
 *
 * // set a target by passing in a LineOfSightTarget to the targets collection
 * losViewModel.targets = [new LineOfSightTarget({
 *    location: new Point({
 *      latitude: 42.352,
 *      longitude: -71.051,
 *      z: 0
 *    })
 * })];
 *
 * // when using JavaScript LineOfSightTarget can be autocast
 * losViewModel.targets = [{
 *    location: {
 *      latitude: 42.352,
 *      longitude: -71.051,
 *      z: 0
 *    }
 * }];
 * ```
 *
 * The view model can also be used to control the different states of the analysis:
 * [start()](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightViewModel/#start) enters the `creation` state where the user can add the observer and
 * targets. [stop()](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightViewModel/#stop) enters the `created` state where the user can't add new targets
 * but they can still move the observer and existing target.
 *
 * ```js
 * viewModel.start();
 * ```
 *
 * The LineOfSightViewModel also allows you to watch for changes (for example
 * when the user moves the observer or the targets) and gives you access to
 * the intersected graphic and the location where the intersection occurred. The
 * observer is just a [Point](https://developers.arcgis.com/javascript/latest/references/core/geometry/Point/), so you can simply watch
 * the observer property on the view model to know when an observer gets added or moved.
 *
 * ```js
 * reactiveUtils.watch(
 *   () => losViewModel.observer,
 *   (value) => {
 *     // do something with the value
 *   }
 * );
 *
 * const highlightHandles = [];
 *
 * reactiveUtils.watch(
 *   () => {
 *     // Watch the whole expression. The callback will be called if any target is added or removed
 *     // and also if any of the target's `intersectedGraphic` property changes.
 *     viewModel.targets
 *       .map((target) => target.intersectedGraphic)
 *       .map((graphic) => !!graphic)
 *       .toArray()
 *   },
 *   (intersectedGraphics) => {
 *     // Remove the previous highlights
 *     highlightHandles.forEach((handle) => handle.remove());
 *
 *     // Highlight all the intersected graphics
 *     highlightHandles = intersectedGraphics.map((graphic) => {
 *       return layerView.highlight(graphic);
 *     });
 *   }
 * );
 * ```
 *
 * A target contains information about the location of the target, whether it's visible or not,
 * which graphic it intersects and the location where it intersects that graphic, an integrated mesh or the ground.
 * The [LineOfSightTarget](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightTarget/) class is used to represent a target.
 *
 * @deprecated since version 4.33. Use the [LineOfSightAnalysis](https://developers.arcgis.com/javascript/latest/references/core/analysis/LineOfSightAnalysis/) or [Line Of Sight component](https://developers.arcgis.com/javascript/latest/references/map-components/components/arcgis-line-of-sight/) instead. For information on widget deprecation, read about [Esri's move to web components](https://developers.arcgis.com/javascript/latest/components-transition-plan/).
 * @since 4.14
 * @see [LineOfSight](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/) widget - _Deprecated since 4.33. Use the [Line Of Sight component](https://developers.arcgis.com/javascript/latest/references/map-components/components/arcgis-line-of-sight/) instead._
 * @see [LineOfSightTarget](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightTarget/)
 * @see [Sample - Line of sight widget](https://developers.arcgis.com/javascript/latest/sample-code/widgets-line-of-sight/)
 * @see [Programming patterns: Widget viewModel pattern](https://developers.arcgis.com/javascript/latest/programming-patterns/#widget-viewmodel-pattern)
 */
export default class LineOfSightViewModel extends Accessor {
  constructor(properties?: LineOfSightViewModelProperties);
  /**
   * The line of sight analysis object being created or modified by the view model.
   *
   * If no analysis is provided, the view model automatically creates its own analysis and adds it to the view. In
   * this case, the analysis will also be automatically removed from the view when the view model is destroyed.
   *
   * @since 4.23
   * @example
   * // Construct a line of sight analysis object outside of the view model
   * const analysis = new LineOfSightAnalysis({
   *   observer: {
   *     type: "point", // autocasts as new Point()
   *     x: 7.67,
   *     y: 45.981,
   *     z: 3435.765
   *   },
   *   targets: [{
   *     location: {
   *       type: "point",
   *       x: 7.659,
   *       y: 45.976,
   *       z: 4437
   *     }
   *   }]
   * });
   *
   * // Ensure that the analysis is added to the view
   * view.analyses.add(analysis);
   *
   * // Frame the analysis in the view
   * view.goTo(analysis.extent);
   *
   * // Pass the analysis object as a constructor parameter to modify it using the view model
   * const viewModel = new LineOfSightViewModel({
   *   analysis: analysis,
   *   view: view
   * });
   */
  get analysis(): LineOfSightAnalysis;
  set analysis(value: LineOfSightAnalysisProperties);
  /**
   * The observer's viewpoint from which lines of sight will be drawn towards the targets. The Z
   * value of the point is an absolute value.
   */
  get observer(): Point | null | undefined;
  set observer(value: PointProperties | null | undefined);
  /**
   * The view model's state.
   *
   * Value | Description
   * ------------|-------------
   * disabled | not ready yet
   * ready | ready for analysis
   * creating | observer/target points are being placed
   * created | finished analysis
   *
   * @default "disabled"
   */
  get state(): "disabled" | "ready" | "creating" | "created";
  /**
   * A collection of [LineOfSightTarget](https://developers.arcgis.com/javascript/latest/references/core/widgets/LineOfSight/LineOfSightTarget/) containing
   * the target location and the analysis results.
   */
  get targets(): Collection<LineOfSightTarget>;
  set targets(value: ReadonlyArrayOrCollection<LineOfSightTargetProperties>);
  /** The view from which the widget will operate. */
  view?: SceneView | null;
  /** Clears the current analysis results. After calling this method, the user can set a new observer and targets. */
  clear(): void;
  /** If stopped, this method continues the line of sight analysis and the user can add more targets. */
  continue(): void;
  /** Starts a new line of sight analysis. */
  start(): Promise<void>;
  /**
   * Stops the current line of sight analysis, keeping the results in the view.
   * Users can still interact with existing targets and the observer but they can't place
   * new target points.
   */
  stop(): void;
}