import type Accessor from "../../core/Accessor.js";
import type { InputParseFunction, InputFormatFunction, LabelFormatFunction, SliderFormatType } from "../types.js";
import type { Bounds, LabelInfos } from "./types.js";

export interface SliderViewModelProperties extends Partial<Pick<SliderViewModel, "effectiveMax" | "effectiveMin" | "inputFormatFunction" | "inputParseFunction" | "labelFormatFunction" | "max" | "min" | "precision" | "thumbsConstrained" | "values">> {}

export type SliderViewModelState = "disabled" | "ready";

/**
 * Provides the logic for the [Slider](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/) widget.
 *
 * @since 4.12
 * @see [Slider](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/)
 * @see [Programming patterns: Widget viewModel pattern](https://developers.arcgis.com/javascript/latest/programming-patterns/#widget-viewmodel-pattern)
 */
export default class SliderViewModel extends Accessor {
  constructor(properties?: SliderViewModelProperties);
  /**
   * When set, the user is restricted from moving slider thumbs to positions higher than
   * this value. This value should be less than the slider [max](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#max).
   * The `effectiveMax` and [effectiveMin](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#effectiveMin) allow you to represent ranges of values in a dataset that cannot
   * be filtered or selected with the slider. This can be useful when using the slider to
   * represent datasets with outliers, or scale ranges not suitable for a layer.
   *
   * @since 4.23
   * @example
   * // slider.viewModel.max = 100
   * slider.viewModel.effectiveMax = 60;
   * // now the user cannot slide thumbs above
   * // 60 even though the slider track displays
   * // values up to 100.
   */
  accessor effectiveMax: number | null | undefined;
  /**
   * When set, the user is restricted from moving slider thumbs to positions less than
   * this value.  This value should be greater than the slider [min](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#min).
   * The `effectiveMin` and [effectiveMax](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#effectiveMax) allow you to represent ranges of values in a dataset that cannot
   * be filtered or selected with the slider. This can be useful when using the slider to
   * represent datasets with outliers, or scale ranges not suitable for a layer.
   *
   * @since 4.23
   * @example
   * // slider.viewModel.min = 0
   * slider.viewModel.effectiveMin = 4;
   * // now the user cannot slide thumbs below
   * // 4 even though the slider track displays
   * // values down to 0.
   */
  accessor effectiveMin: number | null | undefined;
  /**
   * A function used to format user inputs. As opposed to [labelFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labelFormatFunction), which formats
   * thumb labels, the `inputFormatFunction` formats thumb values in the input element when the user begins
   * to edit them.
   *
   * The image below demonstrates how slider input values resemble corresponding slider values by default
   * and won't match the formatting set in `labelFormatFunction`.
   *
   * ![Slider without input formatter](https://developers.arcgis.com/javascript/latest/assets/img/apiref/widgets/sliders/slider-no-input-formatter.png "Slider without input formatter")
   *
   * If you want to format slider input values so they match thumb labels, you can pass the same function set in `labelFormatFunction` to
   * `inputFormatFunction` for consistent formatting.
   *
   * ![Slider with input formatter](https://developers.arcgis.com/javascript/latest/assets/img/apiref/widgets/sliders/slider-input-formatter.png "Slider with input formatter")
   *
   * However, if an `inputFormatFunction` is specified, you must also write a corresponding
   * [inputParseFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#inputParseFunction) to parse user inputs to understandable slider values. In most cases, if
   * you specify an `inputFormatFunction`, you should set the [labelFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labelFormatFunction) to the same value
   * for consistency between labels and inputs.
   *
   * This property overrides the default input formatter, which formats by calling `toString()` on the input value.
   *
   * @see [inputParseFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#inputParseFunction)
   * @example
   * // Formats the slider input to abbreviated numbers with units
   * // e.g. a thumb at position 1500 will render with an input label of 1.5k
   * slider.inputFormatFunction = function(value, type){
   *   if(value >= 1000000){
   *     return (value / 1000000).toPrecision(3) + "m"
   *   }
   *   if(value >= 100000){
   *     return (value / 1000).toPrecision(3) + "k"
   *   }
   *   if(value >= 1000){
   *     return (value / 1000).toPrecision(2) + "k"
   *   }
   *   return value.toFixed(0);
   * }
   */
  accessor inputFormatFunction: InputFormatFunction | null | undefined;
  /**
   * Function used to parse slider inputs formatted by the [inputFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#inputFormatFunction).
   * This property must be set if an `inputFormatFunction` is set. Otherwise the slider values will
   * likely not update to their expected positions.
   *
   * Overrides the default input parses, which is a parsed floating point number.
   *
   * @see [inputFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#inputFormatFunction)
   * @example
   * // Parses the slider input (a string value) to a number value understandable to the slider
   * // This assumes the slider was already configured with an inputFormatFunction
   * // For example, if the input is 1.5k this function will parse
   * // it to a value of 1500
   * slider.inputParseFunction = function(value, type, index){
   *   let charLength = value.length;
   *   let valuePrefix = parseFloat(value.substring(0,charLength-1));
   *   let finalChar = value.substring(charLength-1);
   *
   *   if(parseFloat(finalChar) >= 0){
   *     return parseFloat(value);
   *   }
   *   if(finalChar === "k"){
   *     return valuePrefix * 1000;
   *   }
   *   if(finalChar === "m"){
   *     return valuePrefix * 1000000;
   *   }
   *   return value;
   * }
   */
  accessor inputParseFunction: InputParseFunction | null | undefined;
  /**
   * A function used to format labels. Overrides the default label formatter.
   *
   * By default labels are formatted in the following way:
   *
   * - When the data range is less than `10` (`(max - min) < 10`), labels
   *   are rounded based on the value set in the [precision](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#precision) property.
   * - When the data range is larger than `10`, [labels](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labels) display with a precision of
   *   no more than two decimal places, though actual slider thumb values will maintain the
   *   precision specified in [precision](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#precision).
   *
   * Use this property to override the behavior defined above.
   *
   * @example
   * // For thumb values, rounds each label to whole numbers.
   * // Note the actual value of the thumb continues to be stored
   * // based on the indicated data `precision` despite this formatting
   * sliderViewModel.labelFormatFunction = function(value, type) {
   *   return (type === "value") ? value.toFixed(0) : value;
   * }
   */
  accessor labelFormatFunction: LabelFormatFunction | null | undefined;
  /**
   * An array of strings associated with [values](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) generated using an internal label formatter or
   * the values returned from [labelFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labelFormatFunction).
   */
  get labels(): LabelInfos;
  /**
   * The maximum possible data/thumb value of the slider. In the constructor, if one of the values
   * specified in [values](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) is greater than the `max` value specified
   * in this property, then the `max` will update to the highest value in `values`.
   *
   * @example sliderViewModel.max = 100;
   */
  accessor max: number | null | undefined;
  /**
   * The minimum possible data/thumb value of the slider. In the constructor, if one of the values
   * specified in [values](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) is less than the `min` value specified
   * in this property, then the `min` will update to the lowest value in `values`.
   *
   * @example sliderViewModel.min = 0;
   */
  accessor min: number | null | undefined;
  /**
   * Defines how slider values should be rounded. This number indicates the number
   * of decimal places slider values should round to when they have been moved.
   *
   * This value also indicates the precision of thumb [labels](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labels) when the data range
   * ([max](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#max) - [min](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#min)) is less than `10`.
   *
   * When the data range is larger than `10`, [labels](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labels) display with a precision of
   * no more than two decimal places, though actual slider thumb values will maintain the
   * precision specified in this property.
   *
   * For example, given the default precision of `4`, and the following slider configuration,
   * The label of the thumb will display two decimal places, but the precision of the actual
   * thumb value will not be lost even when the user slides or moves the thumb.
   *
   * ```js
   * const slider = new SliderVM({
   *   min: 20,
   *   max: 100,  // data range of 80
   *   values: [50.4331],
   *   // thumb label will display 50.43
   *   // thumb value will maintain precision, so
   *   // value will remain at 50.4331
   * });
   * ```
   *
   * If the user manually enters a value that has a higher precision than what's indicated by
   * this property, the precision of that thumb value will be maintained until the thumb
   * is moved by the user. At that point, the value will be rounded according to the indicated precision.
   *
   * If thumb labels aren't visible, they must be enabled with [Slider.labelInputsEnabled](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/#labelInputsEnabled).
   *
   * Keep in mind this property rounds thumb [values](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) and shouldn't be used exclusively
   * for formatting purposes. To format thumb `labels`, use the [labelFormatFunction](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#labelFormatFunction)
   * property.
   *
   * @default 4
   * @example sliderViewModel.precision = 7;
   */
  accessor precision: number;
  /** The state of the view model. */
  get state(): SliderViewModelState;
  /**
   * When `false`, the user can freely move any slider thumb to any
   * position along the track. By default, a
   * thumb's position is constrained to the positions of neighboring thumbs so
   * you cannot move one thumb past another. Set this property to `false` to
   * disable this constraining behavior.
   *
   * @default true
   * @example
   * // allows the user to freely move slider
   * // thumbs to any position along the track
   * slider.viewModel.thumbsConstrained = false;
   */
  accessor thumbsConstrained: boolean;
  /**
   * An array of numbers representing absolute thumb positions on the slider.
   *
   * @see [setValue()](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#setValue)
   * @example
   * const slider = new SliderVM({
   *   min: 20,
   *   max: 100,  // data range of 80
   *   values: [50.4331],
   *   // thumb label will display 50.43
   *   // thumb value will maintain precision, so
   *   // value will remain at 50.4331
   * });
   */
  accessor values: number[] | null | undefined;
  /**
   * The default input format function available for use as a fallback in custom formatting implementations.
   *
   * @param value - The input value to format.
   * @returns The formatted input value.
   */
  defaultInputFormatFunction(value: number): string;
  /**
   * The default input parsing function available for use as a fallback in custom parsing implementations.
   *
   * @param value - The thumb value to parse.
   * @returns The parsed thumb value.
   */
  defaultInputParseFunction(value: string): number;
  /**
   * The default label format function, available for use as a fallback in custom formatting implementations.
   *
   * @param value - The thumb value to format.
   * @returns The formatted thumb value.
   */
  defaultLabelFormatFunction(value: number): string;
  /**
   * Returns the effective bounds of the slider. If [effectiveMin](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#effectiveMin) and [effectiveMax](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#effectiveMax)
   * are set on the slider, then those values will be returned. If not, then the [min](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#min) and [max](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#max)
   * are returned.
   *
   * @returns The effective bounds of the slider.
   * @since 4.23
   */
  getBounds(): Bounds;
  /**
   * Returns the min and max bounds for a 'value' at the provided index.
   * Also used internally to provide accessibility information via HTMLElement properties
   *
   * @param index - The index of the associated value.
   * @returns Returns a simple object with bound information.
   */
  getBoundsForValueAtIndex(index: number): Bounds;
  /**
   * Returns the formatted label for a provided value.
   *
   * @param value - The value from which to retrieve a formatted label.
   * @param type - The label type.
   * @param index - The index of the label.
   * @returns Returns the formatted label.
   */
  getLabelForValue(value: number | null | undefined, type?: SliderFormatType, index?: number): string | null | undefined;
  /**
   * Updates a thumb [value](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) based on the provided index. The provided value
   * must differ from the previous value.
   * The provided value must also be between the [min](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#min) and [max](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#max).
   *
   * @param index - The index of the thumb value in the associated [values](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#values) array.
   * @param value - The new value to replace with the old value.
   */
  setValue(index: number, value: number): void;
  /**
   * Rounds the given value to the number of decimal places specified in the [precision](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#precision) property set on the view model.
   *
   * @param value - The thumb value to format.
   * @returns The input value rounded to the number of places specified in [precision](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#precision).
   * @since 4.14
   * @see [precision](https://developers.arcgis.com/javascript/latest/references/core/widgets/Slider/SliderViewModel/#precision)
   */
  toPrecision(value: number): number;
}