/**
 * @since Last modified: 2024/03/04 15:57:45
 * @namespace field
 * @description Operations on multi-value form fields, such as select-multiple, file-multiple, checkbox, radio types.
 * @see {@link https://codepen.io/axui/pen/jOJbJMy|demo @ codepen}
 */
'use strict';
import clampVal from './clampVal';
import getEl from './getEl';
import isEmpty from './isEmpty';
type T_field = {
    getVals: any;
    setVals: any;
    reset: any;
}
const field: T_field = {
    /**
 * @memberOf field
 * @description Get field data of form fields, including multi-valued form fields and sigle-valued form fields.
 * @param {object} obj  - Parameters.
 * @param {getEl} obj.el  - Dom selector of input.
 * @param {""|"string"|"array"} [obj.format=""]  - Output value type.
 * @param {getEl} [obj.parent=el.closest('form')]  - The parent Dom of input.If el itself is not a form field, you need to use this parameter to specify the parent node of the form field.
 * @param {string} [obj.separator=","]  - The separator character of Output value.if format=false,can use the parameter.
 * @returns {array|string} Return a value array of value string.
 * @see {@link https://codepen.io/axui/pen/jOJbJMy|demo @ codepen}
 * @example
 * ax.field.getVals({el:'select'});
 * <!--return ['BJ','SH','TJ']-->
 */
    getVals: ({ el, format = '', parent, separator = ',' }: { el: any, type?: string, format?: '' | 'string' | 'array', parent?: any, separator?: string }): any => {
        let fieldDom = getEl(el),
            values: any[] = [],
            none = format === 'array' ? [] : format === 'string' ? '' : null,
            result: any;
        try {
            if (fieldDom) {
                let fieldType = fieldDom.type,
                    parentDom = getEl(parent) || fieldDom.closest('form') || document.body;
                if (fieldType.includes('select')) {
                    let options = [...fieldDom.options];
                    options.forEach((k: any) => {
                        k.selected && k.getAttribute('value') ? values.push(k.value) : null;
                    });
                } else if (fieldType === 'checkbox' || fieldType === 'radio') {
                    let fields = [...parentDom.querySelectorAll(`[name="${fieldDom.name}"]`)];
                    fields.forEach((k: any) => {
                        k.checked && k.getAttribute('value') ? values.push(k.value) : null;
                    });
                } else if (fieldType === 'file') {
                    values = [...fieldDom.files];
                } else {
                    values = fieldDom.value ? [fieldDom.value] : [];
                }
                if (format === 'string') {
                    result = (fieldType === 'file') ? values.map((k: any) => k.name.trim()).join(separator) : values.join(separator);
                } else if (format === 'array') {
                    result = values;
                } else {
                    result = ['select-multiple', 'checkbox', 'file'].includes(fieldType) ? values : (values[0] || '');
                }
                return result;
            }
        } catch (e) {
            return none;
        }
    },
    /**
    * @memberOf field
    * @description Set the values of select, checkbox and radio. File cannot be set (it is read-only).
    * @param {object} obj  - Parameters.
    * @param {getEl} obj.el  - Dom selector of field.
    * @param {string | number | string[] | number[]} [obj.val]  - The data for setting.
    * @param {getEl} [obj.parent=closet('form')]  - The parent Dom of input.If el itself is not a form field, you need to use this parameter to specify the parent node of the form field.
     * @param {function} [obj.cb]  - The call back after set values.Parameters is fields and values.
    * @returns {null} - Return null.
    * @see {@link https://codepen.io/axui/pen/jOJbJMy|demo @ codepen}
    * @example
    * ax.field.setVals({'#demo',val:['read','travel']});
    * <!--return null-->
    */
    setVals: ({ el, val, parent, cb }: { el: any, val?: string | number | string[] | number[], parent?: any, cb?: T_fn }): void => {
        let fieldDom = getEl(el),
            fieldType = fieldDom?.type,
            values = isEmpty(val) ? [] : (!Array.isArray(val) ? [val] : val),
            parentDom = getEl(parent) || fieldDom?.closest('form') || document.body,
            fields: any = [];
        if (fieldDom) {
            if (fieldType.includes('select')) {
                let options = [...fieldDom.options].filter((k: any) => k.getAttribute('value')),
                    tmp: string[] = values.map((k: any) => k.toString());
                options.forEach((k: any) => {
                    k.selected = tmp.includes(k.value) ? true : false;
                });
                fields = [fieldDom];
            } else if (fieldType === 'checkbox' || fieldType === 'radio') {
                fields = [...parentDom.querySelectorAll(`[name="${fieldDom.name}"]`)].filter((k: any) => k.getAttribute('value'));
                let tmp: string[] = values.map((k: any) => k.toString());
                fields.forEach((k: any) => {
                    k.checked = tmp.includes(k.value) ? true : false;
                });
            } else if (fieldType === 'file') {
                fieldDom.value = '';
                console.warn('The field.files property cannot be changed through scripts,this operation is performed by resetting!');
            } else if (fieldType === 'number') {
                fieldDom.value = clampVal({
                    val: values[0] || 0,
                    min: fieldDom.min,
                    max: fieldDom.max
                });
            } else if (fieldType === 'range') {
                fieldDom.value = values[0] || fieldDom.min || 0;
            } else {
                fieldDom.value = values[0] || '';
            }
            cb && cb();
        }
    },
    /**
    * @memberOf field
    * @description Multi-valued fields reset to zero.
    * @param {getEl} obj.el  - Dom selector of field.
    * @param {boolean} [obj.zero=false]  - Whether to return to zero directly.
    * @param {getEl} [obj.parent=closet('form')]  - The parent Dom of input.If el itself is not a form field, you need to use this parameter to specify the parent node of the form field.
    * @returns {null} - Return null.
    * @see {@link https://codepen.io/axui/pen/jOJbJMy|demo @ codepen}
    * @example
    * ax.field.reset({el:'#demo'});
    * <!--return null-->
    */
    reset: ({ el, parent, zero = false }: { el: any, parent?: any, zero?: boolean }): void => {
        let fieldDom = getEl(el),
            type = fieldDom?.type;
        if (type) {
            if (type?.includes('select')) {
                [...fieldDom.options].forEach((i: any) => i.selected = !zero ? i.defaultSelected : false);
            } else if (type === 'checkbox' || type === 'radio') {
                let parentDom = getEl(parent) || fieldDom?.closest('form') || document.body,
                    fields = [...parentDom.querySelectorAll(`[name="${fieldDom.name}"]`)];
                fields.forEach((k: any) => {
                    k.checked = !zero ? k.defaultChecked : false;
                });
            } else if (['number', 'range'].includes(type)) {
                fieldDom.value = !zero ? fieldDom.defaultValue : (fieldDom.min || 0);
            } else {
                fieldDom.value = !zero ? fieldDom.defaultValue : '';
            }
        }
    }
}
export default field;
