/**
 * The slider is a way to allow the user to select a value from a given numerical range.
 * You might use it for choosing a percentage, combine two of them to get min and max values,
 * or use three of them to specify the hex values for a color. Each slider contains a single
 * 'thumb' that can be dragged along the slider's length to change the value. Sliders are equally
 * useful inside {@link Ext.form.Panel forms} and standalone. Here's how to quickly create a slider
 * in form, in this case enabling a user to choose a percentage:
 *
 * ```javascript
 * @example({ framework: 'extjs' })
 * Ext.create('Ext.form.Panel', {
 *     fullscreen: true,
 *     items: [
 *         {
 *             xtype: 'sliderfield',
 *             label: 'Percentage',
 *             value: 50,
 *             minValue: 0,
 *             maxValue: 100
 *         }
 *     ]
 * });
 * ```
 * ```javascript
 * @example({framework: 'ext-react', packages:['ext-react']})
 * import React, { Component } from 'react';
 * import { ExtContainer, ExtFormPanel, ExtSliderField } from '@sencha/ext-react';
 * export default class SliderFieldExample extends Component {
 *         state = {
 *             singleValue: 20,
 *             multipleValue: [10, 70]
 *         };
 *
 *         onSingleChange = (field, value) => {
 *             this.setState({ singleValue: value });
 *         }
 *
 *         onMultipleChange = (field, value) => {
 *             this.setState({ multipleValue: value });
 *         }
 *
 *         render() {
 *             const { singleValue, multipleValue } = this.state;
 *
 *             return (
 *                 <ExtContainer layout="center">
 *                     <ExtFormPanel shadow width="300">
 *                         <ExtSliderField
 *                             onChange={this.onSingleChange} 
 *                             label="Single Thumb"
 *                             value={singleValue} 
 *                         />
 *                         <div style={{marginBottom: '20px'}}>Value: {singleValue}</div>
 *                         <ExtSliderField
 *                             onChange={this.onMultipleChange} 
 *                             label="Multiple Thumbs"
 *                             values={multipleValue} 
 *                         />
 *                         <div>Values: {multipleValue.join(', ')}</div>
 *                     </ExtFormPanel>
 *                 </ExtContainer>
 *             )
 *         }
 *     }
 * ```
 * ```javascript
 * @example({framework: 'ext-angular', packages:['ext-angular']})
 * import { Component } from '@angular/core'
 * declare var Ext: any;
 *
 * @Component({
 *     selector: 'app-root-1',
 *     styles: [``],
 *     template: `
 *         <ExtContainer [layout]="'center'">
 *             <ExtFormPanel [shadow]="true" [width]="400" [height]="300">
 *                 <ExtSliderField
 *                     (change)="onSingleChange($event)"
 *                     label="Single Thumb"
 *                     [value]="singleValue"
 *                 >
 *                 </ExtSliderField>
 *                 <ExtContainer [style]="{marginBottom: 20}"
 *                      [html]="'Value: '+singleValue">
 *                 </ExtContainer>
 *                 <ExtSliderField
 *                     (change)="onMultipleChange($event)"
 *                     label="Multiple Thumbs"
 *                     [value]="multipleValue"
 *                 >
 *                 </ExtSliderField>
 *                 <ExtContainer
 *                     [html]="'Values: '+multipleValue.join(', ')">
 *                 </ExtContainer>
 *             </ExtFormPanel>
 *         </ExtContainer>
 *     `
 * })
 * export class AppComponent {
 *     singleValue:number = 20;
 *     multipleValue:number[] = [10, 70];
 *
 *     onSingleChange = (params) => {
 *         this.singleValue = params.newValue;
 *     }
 *
 *     onMultipleChange = (params) => {
 *         this.multipleValue = params.newValue;
 *     }
 * }
 * ```
 * ```html
 * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 1 })
 * <ext-container layout="center">
 *    <ext-formpanel
 *         shadow="true"
 *         width="300"
 *    >
 *        <ext-sliderfield
 *             onChange="sliderfield.onSingleChange"
 *             label="Single Thumb"
 *             value="20"
 *             onready="sliderfield.readySingleChangeSliderField"
 *        >
 *        </ext-sliderfield>
 *        <ext-container
 *            style='{marginBottom: "20px"}'
 *            onready="sliderfield.readySingleValueMessage"
 *        >
 *        </ext-container>
 *        <ext-sliderfield
 *             onChange="sliderfield.onMultipleChange"
 *             label="Multiple Thumbs"
 *             value="[10,70]"
 *             onready="sliderfield.readyMultipleChangeSliderField"
 *        >
 *        </ext-sliderfield>
 *        <ext-container
 *             onready="sliderfield.readyMultipleValueMessage"
 *        >
 *        </ext-container>
 *    </ext-formpanel>
 * </ext-container>
 * ```
 * ```javascript
 * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 2 })
 * import '@sencha/ext-web-components/dist/ext-container.component';
 * import '@sencha/ext-web-components/dist/ext-formpanel.component';
 * import '@sencha/ext-web-components/dist/ext-sliderfield.component';
 *
 * export default class SliderFieldComponent {
 *
 *   constructor() {
 *       this.singleValue = 20;
 *       this.multipleValue = [10, 70];
 *   }
 *
 *   onSingleChange = (event) => {
 *       this.singleValueMessageView.setHTML(`Values: ${event.detail.newValue}`)
 *   }
 *
 *   readySingleChangeSliderField = (event) => {
 *       this.singleValueSliderFieldView = event.detail.cmp;
 *   }
 *
 *   readyMultipleChangeSliderField = (event) => {
 *       this.multipleValueSliderFieldView = event.detail.cmp;
 *   }
 *
 *   onMultipleChange = (event) => {
 *       this.multipleValueMessageView.setHTML(`Values: ${event.detail.newValue.join(',')}`)
 *   }
 *
 *   readyMultipleValueMessage = (event) => {
 *        this.multipleValueMessageView = event.detail.cmp;
 *        this.multipleValueMessageView.setHtml(`Values: ${this.multipleValue}`)
 *   }
 *
 *   readySingleValueMessage = (event) => {
 *        this.singleValueMessageView = event.detail.cmp;
 *        this.singleValueMessageView.setHtml(`Value: ${this.singleValue}`);
 *   }
 * }
 *
 *  window.sliderfield = new SliderFieldComponent();
 * ```
 *
 * In this case we set a starting value of 50%, and defined the min and max values to be 0 and
 * 100 respectively, giving us a percentage slider. Because this is such a common use case, the
 * defaults for {@link #minValue} and {@link #maxValue} are already set to 0 and 100 so in the
 * example above they could be removed.
 *
 * It's often useful to render sliders outside the context of a form panel too. In this example
 * we create a slider that allows a user to choose the waist measurement of a pair of jeans.
 * Let's say the online store we're making this for sells jeans with waist sizes from 24 inches
 * to 60 inches in 2 inch increments - here's how we might achieve that:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'sliderfield',
 *                 label: 'Waist Measurement',
 *                 minValue: 24,
 *                 maxValue: 60,
 *                 increment: 2,
 *                 value: 32
 *             }
 *         ]
 *     });
 *
 * Now that we've got our slider, we can ask it what value it currently has and listen
 * to events that it fires. For example, if we wanted our app to show different images
 * for different sizes, we can listen to the {@link #change} event to be informed whenever
 * the slider is moved:
 *
 *     slider.on('change', function(field, newValue) {
 *         if (newValue[0] > 40) {
 *             imgComponent.setSrc('large.png');
 *         } else {
 *             imgComponent.setSrc('small.png');
 *         }
 *     }, this);
 *
 * Here we listened to the {@link #change} event on the slider and updated the background
 * image of an {@link Ext.Img image component} based on what size the user selected. Of
 * course, you can use any logic inside your event listener.
 */
Ext.define('Ext.field.Slider', {
    extend: 'Ext.field.Field',
    xtype: 'sliderfield',
    requires: ['Ext.slider.Slider'],
    alternateClassName: 'Ext.form.Slider',
 
    mixins: [
        'Ext.mixin.ConfigProxy',
        'Ext.field.BoxLabelable'
    ],
 
    /**
     * @event change
     * Fires when the value changes.
     * @param {Ext.field.Slider} me 
     * @param {Number[]} newValue The new value.
     * @param {Number[]} oldValue The old value.
     */
 
    /**
     * @event dragchange
     * Fires when a thumb value changes via drag.
     * @param {Ext.field.Slider} me 
     * @param {Ext.slider.Slider} sl Slider Component.
     * @param {Ext.slider.Thumb} thumb 
     * @param {Number[]} newValue The new value of this thumb.
     * @param {Number[]} oldValue The old value of this thumb.
     */
 
    /**
    * @event dragstart
    * Fires when the slider thumb starts a drag operation.
    * @param {Ext.field.Slider} this 
    * @param {Ext.slider.Slider} sl Slider Component.
    * @param {Ext.slider.Thumb} thumb The thumb being dragged.
    * @param {Array} value The start value.
    * @param {Ext.event.Event} e 
    */
 
    /**
    * @event drag
    * Fires when the slider thumb starts a drag operation.
    * @param {Ext.field.Slider} this 
    * @param {Ext.slider.Slider} sl Slider Component.
    * @param {Ext.slider.Thumb} thumb The thumb being dragged.
    * @param {Ext.event.Event} e 
    */
 
    /**
    * @event dragend
    * Fires when the slider thumb ends a drag operation.
    * @param {Ext.field.Slider} this 
    * @param {Ext.slider.Slider} sl Slider Component.
    * @param {Ext.slider.Thumb} thumb The thumb being dragged.
    * @param {Array} value The end value.
    * @param {Ext.event.Event} e 
    */
 
    config: {
        /**
         * @private
         */
        slider: {
            xtype: 'slider',
            inheritUi: true
        },
 
        /**
         * @cfg {Boolean} liveUpdate
         * `true` to fire change events while the slider is dragging. `false` will
         * only fire a change once the drag is complete.
         */
        liveUpdate: false,
 
        /**
         * @cfg tabIndex
         * @inheritdoc
         */
        tabIndex: -1,
 
        /**
         * @cfg readOnly
         * Will make this field read only, meaning it cannot be changed with used interaction.
         * @accessor
         */
        readOnly: false,
 
        /**
         * @cfg value
         * @inheritdoc Ext.slider.Slider#cfg-value
         * @accessor
         */
        value: 0
    },
 
    /**
     * @property classCls
     * @inheritdoc
     */
    classCls: Ext.baseCSSPrefix + 'sliderfield',
 
    proxyConfig: {
        slider: [
            /**
             * @cfg increment
             * @inheritdoc Ext.slider.Slider#increment
             */
            'increment',
 
            /**
             * @cfg minValue
             * @inheritdoc Ext.slider.Slider#minValue
             */
            'minValue',
 
            /**
             * @cfg maxValue
             * @inheritdoc Ext.slider.Slider#maxValue
             */
            'maxValue'
        ]
    },
 
    /**
     * @cfg bodyAlign
     * @inheritdoc
     */
    bodyAlign: 'stretch',
 
    /**
     * @property defaultBindProperty
     * @inheritdoc
     */
    defaultBindProperty: 'value',
 
    /**
     * @cfg twoWayBindable
     * @inheritdoc
     */
    twoWayBindable: {
        values: 1,
        value: 1
    },
 
    /**
     * @cfg values
     * @inheritdoc Ext.slider.Slider#cfg-values
     */
 
    constructor: function(config) {
        config = config || {};
 
        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }
 
        this.callParent([config]);
        this.updateMultipleState();
    },
 
    /**
     * @private
     */
    initialize: function() {
        this.callParent();
 
        this.getSlider().on({
            scope: this,
 
            change: 'onSliderChange',
            dragstart: 'onSliderDragStart',
            drag: 'onSliderDrag',
            dragend: 'onSliderDragEnd'
        });
    },
 
    getBodyTemplate: function() {
        return this.mixins.boxLabelable.getBodyTemplate.call(this);
    },
 
    applySlider: function(slider) {
        if (slider && !slider.isInstance) {
            slider = this.mergeProxiedConfigs('slider', slider);
            slider.$initParent = this;
            slider = Ext.create(slider);
            delete slider.$initParent;
        }
 
        this.boxElement.appendChild(slider.el);
 
        slider.ownerCmp = this;
 
        return slider;
    },
 
    updateSlider: function(slider) {
        slider.doInheritUi();
    },
 
    getValue: function() {
        return this._value;
    },
 
    applyValue: function(value, oldValue) {
        value = this.callParent([value, oldValue]) || 0;
 
        // If we are currently dragging, don't allow the binding
        // to push a value over the top of what the user is doing.
        if (this.dragging && this.isSyncing('value')) {
            value = undefined;
        }
        else if (Ext.isArray(value)) {
            value = value.slice(0);
 
            if (oldValue && Ext.Array.equals(value, oldValue)) {
                value = undefined;
            }
        }
        else {
            value = [value];
        }
 
        return value;
    },
 
    updateValue: function(value, oldValue) {
        if (!this.dragging) {
            value = this.setSliderValue(value);
        }
 
        this.callParent([value, oldValue]);
    },
 
    setSliderValue: function(value) {
        // Get the value back out after setting
        return this.getSlider().setValue(value).getValue();
    },
 
    onSliderChange: function(slider, thumb, newValue, oldValue) {
        this.setValue(slider.getValue());
        this.fireEvent('dragchange', this, slider, thumb, newValue, oldValue);
    },
 
    onSliderDragStart: function(slider, thumb, startValue, e) {
        this.dragging = true;
        this.fireEvent('dragstart', this, slider, thumb, startValue, e);
    },
 
    onSliderDrag: function(slider, thumb, value, e) {
        var me = this;
 
        if (me.getLiveUpdate()) {
            me.setValue(slider.getValue());
        }
 
        me.fireEvent('drag', me, slider, thumb, value, e);
    },
 
    onSliderDragEnd: function(slider, thumb, startValue, e) {
        this.dragging = false;
        this.fireEvent('dragend', this, slider, thumb, startValue, e);
    },
 
    /**
     * Convenience method. Calls {@link #setValue}.
     * @param {Object} value 
     */
    setValues: function(value) {
        this.setValue(value);
        this.updateMultipleState();
    },
 
    /**
     * Convenience method. Calls {@link #getValue}
     * @return {Object} 
     */
    getValues: function() {
        return this.getValue();
    },
 
    reset: function() {
        var config = this.config,
            initialValue = (this.config.hasOwnProperty('values')) ? config.values : config.value;
 
        this.setValue(initialValue);
    },
 
    updateReadOnly: function(newValue) {
        this.getSlider().setReadOnly(newValue);
    },
 
    updateMultipleState: function() {
        var value = this.getValue();
 
        if (value && value.length > 1) {
            this.addCls(Ext.baseCSSPrefix + 'slider-multiple');
        }
    },
 
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([disabled, oldDisabled]);
 
        this.getSlider().setDisabled(disabled);
    },
 
    doDestroy: function() {
        this.getSlider().destroy();
        this.callParent();
    },
 
    getRefItems: function(deep) {
        var refItems = [],
            slider = this.getSlider();
 
        if (slider) {
            refItems.push(slider);
 
            if (deep && slider.getRefItems) {
                refItems.push.apply(refItems, slider.getRefItems(deep));
            }
        }
 
        return refItems;
    },
 
    rawToValue: Ext.emptyFn
});