/**
 * DevExtreme (ui/drop_down_box.js)
 * Version: 18.2.3 (build 18334)
 * Build date: Fri Nov 30 2018
 *
 * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
var DropDownEditor = require("./drop_down_editor/ui.drop_down_editor"),
    DataExpressionMixin = require("./editor/ui.data_expression"),
    commonUtils = require("../core/utils/common"),
    window = require("../core/utils/window").getWindow(),
    map = require("../core/utils/iterator").map,
    selectors = require("./widget/selectors"),
    KeyboardProcessor = require("./widget/ui.keyboard_processor"),
    deferredUtils = require("../core/utils/deferred"),
    when = deferredUtils.when,
    Deferred = deferredUtils.Deferred,
    $ = require("../core/renderer"),
    eventsEngine = require("../events/core/events_engine"),
    grep = require("../core/utils/common").grep,
    extend = require("../core/utils/extend").extend,
    registerComponent = require("../core/component_registrator");
var DROP_DOWN_BOX_CLASS = "dx-dropdownbox",
    ANONYMOUS_TEMPLATE_NAME = "content";
var DropDownBox = DropDownEditor.inherit({
    _supportedKeys: function() {
        return extend({}, this.callBase(), {
            tab: function(e) {
                if (!this.option("opened")) {
                    return
                }
                var $tabbableElements = this._getTabbableElements(),
                    $focusableElement = e.shiftKey ? $tabbableElements.last() : $tabbableElements.first();
                $focusableElement && eventsEngine.trigger($focusableElement, "focus");
                e.preventDefault()
            }
        })
    },
    _getTabbableElements: function() {
        return this._getElements().filter(selectors.tabbable)
    },
    _getElements: function() {
        return $(this.content()).find("*")
    },
    _getAnonymousTemplateName: function() {
        return ANONYMOUS_TEMPLATE_NAME
    },
    _getDefaultOptions: function() {
        return extend(this.callBase(), {
            acceptCustomValue: false,
            contentTemplate: null,
            dropDownOptions: {},
            openOnFieldClick: true,
            valueFormat: function(value) {
                return Array.isArray(value) ? value.join(", ") : value
            }
        })
    },
    _initMarkup: function() {
        this._initDataExpressions();
        this._renderSubmitElement();
        this.$element().addClass(DROP_DOWN_BOX_CLASS);
        this.callBase()
    },
    _renderSubmitElement: function() {
        this._$submitElement = $("<input>").attr("type", "hidden").appendTo(this.$element())
    },
    _renderValue: function() {
        this._setSubmitValue();
        this.callBase()
    },
    _setSubmitValue: function() {
        var value = this.option("value"),
            submitValue = "this" === this.option("valueExpr") ? this._displayGetter(value) : value;
        this._$submitElement.val(submitValue)
    },
    _getSubmitElement: function() {
        return this._$submitElement
    },
    _renderInputValue: function() {
        var callBase = this.callBase.bind(this),
            values = [];
        if (!this._dataSource) {
            callBase(values);
            return
        }
        var currentValue = this._getCurrentValue(),
            keys = commonUtils.ensureDefined(currentValue, []);
        keys = Array.isArray(keys) ? keys : [keys];
        var itemLoadDeferreds = map(keys, function(key) {
            return this._loadItem(key).always(function(item) {
                var displayValue = this._displayGetter(item);
                values.push(commonUtils.ensureDefined(displayValue, key))
            }.bind(this))
        }.bind(this));
        when.apply(this, itemLoadDeferreds).always(function() {
            this.option("displayValue", values);
            callBase(values.length && values)
        }.bind(this)).fail(callBase);
        return itemLoadDeferreds
    },
    _loadItem: function(value) {
        var deferred = new Deferred,
            that = this;
        var selectedItem = grep(this.option("items") || [], function(item) {
            return this._isValueEquals(this._valueGetter(item), value)
        }.bind(this))[0];
        if (void 0 !== selectedItem) {
            deferred.resolve(selectedItem)
        } else {
            this._loadValue(value).done(function(item) {
                deferred.resolve(item)
            }).fail(function(args) {
                if (that.option("acceptCustomValue")) {
                    deferred.resolve(value)
                } else {
                    deferred.reject()
                }
            })
        }
        return deferred.promise()
    },
    _updatePopupWidth: function() {
        this._setPopupOption("width", this.$element().outerWidth())
    },
    _popupElementTabHandler: function(e) {
        if ("tab" !== e.key) {
            return
        }
        var $firstTabbable = this._getTabbableElements().first().get(0),
            $lastTabbable = this._getTabbableElements().last().get(0),
            $target = e.originalEvent.target,
            moveBackward = !!($target === $firstTabbable && e.shift),
            moveForward = !!($target === $lastTabbable && !e.shift);
        if (moveBackward || moveForward) {
            this.close();
            eventsEngine.trigger(this._input(), "focus");
            if (moveBackward) {
                e.originalEvent.preventDefault()
            }
        }
    },
    _renderPopup: function(e) {
        this.callBase();
        this._options.dropDownOptions = extend({}, this._popup.option());
        this._popup.on("optionChanged", function(e) {
            this.option("dropDownOptions." + e.fullName, e.value)
        }.bind(this));
        if (this.option("focusStateEnabled")) {
            this._popup._keyboardProcessor.push(new KeyboardProcessor({
                element: this.content(),
                handler: this._popupElementTabHandler,
                context: this
            }))
        }
    },
    _popupConfig: function() {
        return extend(this.callBase(), {
            width: function() {
                return this.$element().outerWidth()
            }.bind(this),
            height: "auto",
            tabIndex: -1,
            dragEnabled: false,
            focusStateEnabled: this.option("focusStateEnabled"),
            maxHeight: this._getMaxHeight.bind(this)
        }, this.option("dropDownOptions"))
    },
    _getMaxHeight: function() {
        var $element = this.$element(),
            offsetTop = $element.offset().top - $(window).scrollTop(),
            offsetBottom = $(window).innerHeight() - offsetTop - $element.outerHeight(),
            maxHeight = .9 * Math.max(offsetTop, offsetBottom);
        return maxHeight
    },
    _popupShownHandler: function() {
        this.callBase();
        var $firstElement = this._getTabbableElements().first();
        eventsEngine.trigger($firstElement, "focus")
    },
    _popupOptionChanged: function(args) {
        var options = {};
        if (args.name === args.fullName) {
            options = args.value
        } else {
            var option = args.fullName.split(".").pop();
            options[option] = args.value
        }
        this._setPopupOption(options);
        if (Object.keys(options).indexOf("width") !== -1 && void 0 === options.width) {
            this._updatePopupWidth()
        }
    },
    _setCollectionWidgetOption: commonUtils.noop,
    _optionChanged: function(args) {
        this._dataExpressionOptionChanged(args);
        switch (args.name) {
            case "width":
                this.callBase(args);
                this._updatePopupWidth();
                break;
            case "dropDownOptions":
                this._popupOptionChanged(args);
                break;
            case "dataSource":
                this._renderInputValue();
                break;
            case "displayValue":
                this.option("text", args.value);
                break;
            case "displayExpr":
                this._renderValue();
                break;
            default:
                this.callBase(args)
        }
    }
}).include(DataExpressionMixin);
registerComponent("dxDropDownBox", DropDownBox);
module.exports = DropDownBox;
module.exports.default = module.exports;
