// 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.

Ext.define('Shopware.apps.ViisonPickwareMobilePickProfiles.view.edit.RuleForm', {

    extend: 'Ext.form.Panel',
    alias: 'widget.viison_pickware_mobile_pick_profiles-edit-rule_form',
    cls: 'viison_pickware_mobile_pick_profiles-edit-rule_form',

    mixins: [
        'Shopware.apps.ViisonCommonApp.Mixin',
    ],
    viisonConfigNamespace: 'ViisonPickwareMobilePickProfiles',
    viisonSnippetNamespace: 'backend/viison_pickware_mobile_pick_profiles/main',

    layout: {
        type: 'hbox',
        align: 'stretch',
    },

    /**
     * @property {Object|null}
     */
    ruleDescription: null,
    /**
     * @property {Boolean}
     */
    isSettingRuleDescription: false,

    /**
     * @override
     */
    initComponent: function () {
        this.items = this.createItems();

        this.callParent(arguments);

        this.setRuleDescription(this.ruleDescription);
    },

    createItems: function () {
        return Ext.Array.merge(
            [
                {
                    xtype: 'label',
                    text: this.getViisonSnippet('edit/rule_form/reference_field/label') + ':',
                    cls: 'x-form-item-label',
                    margin: '5px 10px 0px 5px',
                },
                this.createReferenceComboBox(),
                this.createGroupingConstraintComboBox(),
                {
                    xtype: 'label',
                    text: this.getViisonSnippet('edit/rule_form/condition/label') + ':',
                    cls: 'x-form-item-label',
                    margin: '5px 10px 0px 20px',
                },
                this.createLeftOperandComboBox(),
                this.createOperatorComboBox(),
            ],
            this.createRightOperandFields()
        );
    },

    /**
     * @return {Ext.Component}
     */
    getRightOperandField: function () {
        var fields = Ext.Object.getValues(this.rightOperandFields);
        for (var i = 0; i < fields.length; i += 1) {
            if (!fields[i].isHidden()) {
                return fields[i];
            }
        }

        return null;
    },

    /**
     * Resets the internal state of the rght operand field, by hiding all fields that can be the right operator field.
     */
    resetRightOperandField: function () {
        Ext.Object.getValues(this.rightOperandFields).forEach(function (field) {
            field.hide();
        });
    },

    /**
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createReferenceComboBox: function () {
        var referenceStore = Ext.create('Shopware.apps.ViisonPickwareMobilePickProfiles.store.rule.Reference');
        this.referenceComboBox = Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'reference',
            changeEventName: 'referenceChanged',
            store: referenceStore,
            displayField: 'name',
            allowBlank: false,
            hideLabel: true,
            flex: 2,
            maxWidth: 250,
        });

        return this.referenceComboBox;
    },

    /**
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createGroupingConstraintComboBox: function () {
        this.groupingConstraintComboBox = Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'groupingConstraint',
            changeEventName: 'groupingConstraintChanged',
            store: Ext.create('Ext.data.Store', {
                fields: [
                    'id',
                    'name',
                ],
                data: [],
            }),
            displayField: 'name',
            allowBlank: false,
            hideLabel: true,
            width: 120,
            hidden: true,
        });

        return this.groupingConstraintComboBox;
    },

    /**
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createLeftOperandComboBox: function () {
        var leftOperandStore = Ext.create('Ext.data.Store', {
            model: 'Shopware.apps.ViisonPickwareMobilePickProfiles.model.rule.Field',
            data: [],
        });
        this.leftOperandComboBox = Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'leftOperand',
            changeEventName: 'leftOperandChanged',
            store: leftOperandStore,
            displayField: 'name',
            allowBlank: false,
            hideLabel: true,
            flex: 2,
            maxWidth: 250,
        });

        return this.leftOperandComboBox;
    },

    /**
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createOperatorComboBox: function () {
        var operatorStore = Ext.create('Shopware.apps.ViisonPickwareMobilePickProfiles.store.rule.Operator');
        this.operatorComboBox = Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'operator',
            changeEventName: 'operatorChanged',
            store: operatorStore,
            displayField: 'name',
            valueField: 'value',
            allowBlank: false,
            hideLabel: true,
            width: 125,
        });

        return this.operatorComboBox;
    },

    /**
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createRightOperandFields: function () {
        this.rightOperandFields = {};

        // Boolean field
        this.rightOperandFields.booleanField = Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'rightOperandBooleanField',
            operandValueType: 'boolean',
            store: Ext.create('Ext.data.Store', {
                fields: [
                    'value',
                    'name',
                ],
                data: [
                    { value: true, name: this.getViisonSnippet('rule/boolean_value/true') },
                    { value: false, name: this.getViisonSnippet('rule/boolean_value/false') },
                ],
            }),
            displayField: 'name',
            valueField: 'value',
            allowBlank: false,
            hideLabel: true,
            flex: 2,
            maxWidth: 100,
            hidden: true,
            listeners: {
                scope: this,
                comboBoxValueChanged: function (newValue, comboBox) {
                    // Pass on the raw value instead of the record
                    this.fireEvent('rightOperandChanged', comboBox.getValue(), comboBox);
                },
            },
        });

        // Number field
        this.rightOperandFields.numberField = Ext.create('Ext.form.field.Number', {
            name: 'rightOperandNumberField',
            operandValueType: 'number',
            allowBlank: false,
            hideLabel: true,
            flex: 2,
            maxWidth: 100,
            hidden: true,
            listeners: {
                scope: this,
                change: function (field, newValue, oldValue) {
                    if (newValue !== oldValue) {
                        this.fireEvent('rightOperandChanged', newValue, field);
                    }
                },
            },
        });

        // Text field
        this.rightOperandFields.textField = Ext.create('Ext.form.field.Text', {
            name: 'rightOperandTextField',
            operandValueType: 'text',
            allowBlank: true,
            hideLabel: true,
            flex: 2,
            maxWidth: 250,
            hidden: true,
            listeners: {
                scope: this,
                change: function (field, newValue, oldValue) {
                    if (newValue !== oldValue) {
                        this.fireEvent('rightOperandChanged', newValue, field);
                    }
                },
            },
        });

        // Combo box container (combo boxes cannot be reconfigured after creation, hence we add and remove them to/from
        // the container as necessary)
        this.rightOperandFields.comboBoxContainer = Ext.create('Ext.container.Container', {
            layout: 'hbox',
            operandValueType: 'store',
            items: [],
            hidden: true,
            flex: 2,
            maxWidth: 250,
        });

        return Ext.Object.getValues(this.rightOperandFields);
    },

    /**
     * @param {object} ruleDescription
     */
    setRuleDescription: function (ruleDescription) {
        var displayableRuleTypes = [
            'comparison',
            'unary_operation',
        ];
        if (!ruleDescription || displayableRuleTypes.indexOf(ruleDescription.type) === -1) {
            return;
        }
        if (typeof ruleDescription.leftOperand !== 'object' || typeof ruleDescription.operator !== 'string' || typeof ruleDescription.rightOperand !== 'object') {
            throw new Error('The given rule description is invalid.');
        }

        this.ruleDescription = ruleDescription;

        this.isSettingRuleDescription = true;

        // Select the selected reference, which automatically updates all other form fields
        this.referenceComboBox.initialValue = this.ruleDescription.leftOperand.tableName;
        this.referenceComboBox.reset();

        this.isSettingRuleDescription = false;
    },

    /**
     * Updates the visible right operand field based on the type of the left operand.
     */
    updateRightOperandField: function () {
        this.resetRightOperandField();

        if (this.ruleDescription.type !== 'comparison') {
            return;
        }

        // Prepare and show the right field of the required type
        var leftOperand = this.leftOperandComboBox.getSelectedRecord();
        var leftOperandValueType = leftOperand.get('valueType');
        var rightOperand = this.ruleDescription.rightOperand.value;
        switch (leftOperandValueType) {
            case 'boolean':
                this.rightOperandFields.booleanField.setValue(rightOperand);
                this.rightOperandFields.booleanField.show();
                break;
            case 'date':
            case 'number':
                this.rightOperandFields.numberField.setValue(rightOperand);
                this.rightOperandFields.numberField.show();
                break;
            case 'store':
                var storeConfig = leftOperand.get('store');
                // Only create a new combo box, if none exists or a different store is required
                var existingComboBox = this.rightOperandFields.comboBoxContainer.items.first();
                if (existingComboBox && Ext.getClass(existingComboBox.store) === storeConfig.name) {
                    existingComboBox.initialValue = rightOperand;
                    existingComboBox.reset();
                } else {
                    // Create and add new combo box
                    this.rightOperandFields.comboBoxContainer.removeAll();
                    this.rightOperandFields.comboBoxContainer.add(
                        this.createComboBoxWithStoreConfig(storeConfig, rightOperand)
                    );
                    this.rightOperandFields.comboBoxContainer.show();
                }
                break;
            case 'text':
                this.rightOperandFields.textField.setValue(rightOperand);
                this.rightOperandFields.textField.show();
                break;
            default:
                throw new Error('Invalid left operand type "' + leftOperandValueType + '"');
        }
    },

    /**
     * @param {object} storeConfig
     * @param {mixed} initialValue
     * @return {Shopware.apps.ViisonCommonComboBox.view.ComboBox}
     */
    createComboBoxWithStoreConfig: function (storeConfig, initialValue) {
        var resolvedStoreConfig = {
            autoLoad: true,
        };
        if (storeConfig.filters) {
            resolvedStoreConfig.filters = storeConfig.filters;
        }

        return Ext.create('Shopware.apps.ViisonCommonComboBox.view.ComboBox', {
            name: 'rightOperandComboBox',
            store: Ext.create(storeConfig.name, resolvedStoreConfig),
            valueField: storeConfig.valueField || 'id',
            displayField: storeConfig.displayField || 'name',
            initialValue: initialValue,
            allowBlank: false,
            multiSelect: true,
            hideLabel: true,
            flex: 1,
            listeners: {
                scope: this,
                comboBoxValueChanged: function (newValue, comboBox) {
                    // Pass on the raw values instead of the records
                    var value = (newValue) ? comboBox.getValue() : null;
                    this.fireEvent('rightOperandChanged', value, comboBox);
                },
            },
        });
    },

    /**
     * @override
     *
     * We need to override this method, because our form always contains hidden fields, which need to be excluded from
     * the validation. Hence we just copied the implementation from `Ext.form.Basic.isValid()` and added an extra field
     * filter for `isVisible()`.
     */
    isValid: function () {
        Ext.suspendLayouts();
        var invalidFields = this.form.getFields().filterBy(function (field) {
            return field.isVisible() && !field.validate();
        });
        Ext.resumeLayouts(true);

        return invalidFields.length < 1;
    },
});
