// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

/**
 * A combobox that is customised for the selection a store record. That can use a custom template
 * for displaying the records in the box and field and fires more accurate change events, only
 * when the selected record is changed. This component requires only a small configuration to work.
 * This configuration consists only of an instance of 'Ext.data.Store', which must be set and loaded
 * either when instantiating the component or before calling the parent implementation of
 * 'initComponent()'. If you like to allow an empty selection to e.g. apply a filter on all records
 * in the store, you can set 'allowBlank' to 'true', which allows deleting the content of the
 * combobox. In that case a 'comboBoxValueChanged' event with a NULL value is fired.
 */
Ext.define('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {

    extend: 'Ext.form.field.ComboBox',
    alias: 'widget.viison_common_combo_box-combo_box',
    width: 300,
    queryMode: 'local',
    valueField: 'id',
    allowBlank: false,
    editable: false,
    autoSelect: true,
    forceSelection: true,
    submitEmptyText: false,
    /**
     * Override this field to fire an event with a custom name, when the value of the combobox changes.
     */
    changeEventName: 'comboBoxValueChanged',

    /**
     * @Override
     */
    initComponent: function () {
        // Update config
        this.editable = this.allowBlank;
        this.autoSelect = !this.allowBlank;
        this.forceSelection = !this.allowBlank;

        // Listen on value changes to fire a custom event that contains the selected record as an argument
        this.on('change', function (combo, newValue, oldValue) {
            if (newValue === null) {
                // Since newValue is null, we don't compare it to oldValue to allow firing the the custom change event
                // manually. That is, in 'updateSelectedValue()' a 'change' event is explicitly fired on the combobox,
                // if it is allows to be empty. If that happens before another value has been selected, both oldValue
                // and newValue are null, which should still result in this combobox firing its custom change event.
                this.fireEvent(this.changeEventName, null, combo);
            } else if (newValue !== oldValue) {
                // Try to find record
                if (Array.isArray(newValue)) {
                    // Multi select
                    var records = newValue.map(function (value) {
                        return this.findRecordByValue(value);
                    }, this);
                    if (records.length > 0) {
                        this.fireEvent(this.changeEventName, records, combo, oldValue);
                    }
                } else {
                    // Single select
                    var record = this.findRecordByValue(newValue);
                    if (record) {
                        this.fireEvent(this.changeEventName, record, combo, oldValue);
                    }
                }
            }
        }, this);

        // We use this function to circumvent an ExtJS bug which removes the `this.store` property under
        // mysterious circumstances.
        var refreshFunction = function (store) {
            if (!this.store) {
                this.store = store;
            }
            this.updateSelectedValue();
        };

        // Listen on the store's 'clear' and 'refresh' events to update the selected value
        this.store.on('clear', refreshFunction, this);
        this.store.on('refresh', refreshFunction, this);

        this.callParent(arguments);
    },

    /**
     * @Override
     *
     * The parent implementation of this method clears the field even if 'allowBlank'
     * is set to false, hence we have to select the initial value (or first record) again
     * to avoid an empty field even if it should not be empty.
     */
    reset: function () {
        this.callParent();
        this.value = null;
        if (!this.allowBlank) {
            this.updateSelectedValue();
        }
    },

    /**
     * @return Ext.data.Model|false
     */
    getSelectedRecord: function () {
        return this.findRecordByValue(this.getValue());
    },

    /**
     * Sets the value for this combo box either to the configured initial value, the first record of the store or 0,
     * if the store is empty. If the current value (actual 'value' property) is already set and is at least partly valid
     * (i.e. it matches at least one record in the store), the value is changed to the valid part of the current value.
     */
    updateSelectedValue: function () {
        if (this.value !== null && this.value !== undefined) {
            // Filter the current value and keep only those that are available in the configured store
            var validValues = Ext.Array.from(this.value).filter(function (value) {
                return this.findRecordByValue(value) !== false;
            }, this);
            if (validValues.length > 0) {
                this.setValue(validValues);

                return;
            }
        }

        if (this.initialValue) {
            // Filter the initial value(s) and keep only those that are available in the configured store
            var initialValues = Ext.Array.from(this.initialValue).filter(function (value) {
                return this.findRecordByValue(value) !== false;
            }, this);
            delete this.initialValue;
            if (initialValues.length > 0) {
                this.setValue(initialValues);

                return;
            }
        }

        if (this.allowBlank) {
            // Clear the value
            var oldValue = this.getValue();
            this.setValue(null);
            this.fireEvent('change', this, null, oldValue);

            return;
        }

        // Just use the first element for the store as fallback, if available
        if (this.store) {
            var value = (this.store.count() > 0) ? this.store.first().get(this.valueField) : 0;
            this.setValue(value);
        } else {
            this.value = null;
        }
    },

});
