import { __assign, __awaiter, __extends, __generator, __rest } from "tslib";
import { jsx as _jsx } from "react/jsx-runtime";
import cx from 'classnames';
import { Component, createRef } from 'react';
import { FormProvider, useField, useFieldArray, useFieldSet, field, set, array, form, FieldValue, FieldSetValue, useFieldArrayChildModels, ValidateOption, createAsyncValidator, isAsyncValidator, useFieldValue, FieldValid, useFieldValid, useModelValid, useModelValue, useNamedChildModel, } from './formulr';
import memorize from '../utils/memorize-one';
import { FormChildrenContext, } from './context';
import { useForm, useFormValue, useFormValid } from './ZentForm';
import { smoothScroll } from '../utils/scroll';
import { CombineErrors } from './CombineErrors';
import { ValidateOccasion, TouchWhen } from './shared';
import { Disabled } from '../disabled';
import getScrollPosition from '../utils/dom/getScollPosition';
import isPromise from '../utils/isPromise';
export { isViewDrivenProps, ValidateOccasion, } from './shared';
function makeChildrenContext(children) {
    return {
        children: children,
    };
}
var Form = (function (_super) {
    __extends(Form, _super);
    function Form() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.formRef = createRef();
        _this.children = [];
        _this.getChildrenContext = memorize(makeChildrenContext);
        _this.submitSubscription = null;
        _this.resetSubscription = null;
        _this.onSubmit = function (e) {
            e.preventDefault();
            e.stopPropagation();
            _this.props.form.submit(e);
        };
        _this.onReset = function (e) {
            e.preventDefault();
            e.stopPropagation();
            _this.props.form.reset(e);
        };
        _this.onKeyDown = function (e) {
            var _a = _this.props, onKeyDown = _a.onKeyDown, _b = _a.disableEnterSubmit, disableEnterSubmit = _b === void 0 ? true : _b;
            if (disableEnterSubmit &&
                e.key === 'Enter' &&
                e.target.tagName === 'INPUT') {
                e.preventDefault();
                e.stopPropagation();
            }
            onKeyDown && onKeyDown(e);
        };
        _this.submitListener = function (e) {
            _this.submit(e);
        };
        _this.resetListener = function (e) {
            _this.reset(e);
        };
        return _this;
    }
    Form.prototype.reset = function (e) {
        var _a = this.props, form = _a.form, onReset = _a.onReset;
        form.resetValue();
        onReset === null || onReset === void 0 ? void 0 : onReset(e);
    };
    Form.prototype.submit = function (e) {
        return __awaiter(this, void 0, void 0, function () {
            var _a, onSubmit, form, onSubmitFail, onSubmitSuccess, scrollToError, success, fail, error_1;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        _a = this.props, onSubmit = _a.onSubmit, form = _a.form, onSubmitFail = _a.onSubmitFail, onSubmitSuccess = _a.onSubmitSuccess, scrollToError = _a.scrollToError;
                        if (!onSubmit) {
                            return [2];
                        }
                        success = function () {
                            onSubmitSuccess && onSubmitSuccess();
                            form.submitSuccess();
                        };
                        fail = function (error) {
                            onSubmitFail && onSubmitFail(error);
                            form.submitError();
                        };
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 4, , 5]);
                        form.submitStart();
                        return [4, form.validate(ValidateOption.IncludeAsync |
                                ValidateOption.IncludeChildrenRecursively |
                                ValidateOption.IncludeUntouched)];
                    case 2:
                        _b.sent();
                        if (!form.isValid()) {
                            scrollToError && this.scrollToFirstError();
                            fail(new FormValidationError('Form validation failed'));
                            return [2];
                        }
                        return [4, onSubmit(form, e)];
                    case 3:
                        _b.sent();
                        success();
                        return [3, 5];
                    case 4:
                        error_1 = _b.sent();
                        fail(error_1);
                        return [3, 5];
                    case 5: return [2];
                }
            });
        });
    };
    Form.prototype.scrollToFirstError = function () {
        var _this = this;
        var _a = this.props, willScrollToError = _a.willScrollToError, form = _a.form;
        if (typeof willScrollToError !== 'function') {
            this._scrollToFirstError();
        }
        else {
            var p = willScrollToError(form);
            if (!isPromise(p)) {
                this._scrollToFirstError(p);
            }
            else {
                p.then(function (opt) {
                    _this._scrollToFirstError(opt);
                }).catch(function () { });
            }
        }
    };
    Form.prototype._scrollToFirstError = function (options) {
        var scrollX = Infinity;
        var scrollY = Infinity;
        for (var i = 0; i < this.children.length; i += 1) {
            var child = this.children[i];
            var el = child.getDOMNode();
            if (!el || child.valid()) {
                continue;
            }
            var elementBound = el.getBoundingClientRect();
            var y = elementBound.top;
            var x = elementBound.left;
            if (y < scrollY || (y === scrollY && x < scrollX)) {
                scrollX = x;
                scrollY = y;
            }
        }
        if (scrollX === Infinity || scrollY === Infinity) {
            return;
        }
        var scrollOptions = (options !== null && options !== void 0 ? options : {});
        var _a = scrollOptions.offsetX, offsetX = _a === void 0 ? 0 : _a, _b = scrollOptions.offsetY, offsetY = _b === void 0 ? 0 : _b, scrollContainer = scrollOptions.scrollContainer;
        if (scrollContainer) {
            var containerBox = scrollContainer.getBoundingClientRect();
            var deltaX = scrollX - containerBox.left;
            var deltaY = scrollY - containerBox.top;
            var x = scrollContainer.scrollLeft + deltaX + offsetX;
            var y = scrollContainer.scrollTop + deltaY + offsetY;
            smoothScroll(scrollContainer, x, y);
        }
        else {
            var _c = getScrollPosition(), x = _c.x, y = _c.y;
            smoothScroll(document.body, scrollX + x + offsetX, scrollY + y + offsetY);
        }
    };
    Form.prototype.subscribe = function () {
        var form = this.props.form;
        this.submitSubscription = form.submit$.subscribe(this.submitListener);
        this.resetSubscription = form.reset$.subscribe(this.resetListener);
    };
    Form.prototype.unsubscribe = function () {
        if (this.submitSubscription) {
            this.submitSubscription.unsubscribe();
            this.submitSubscription = null;
        }
        if (this.resetSubscription) {
            this.resetSubscription.unsubscribe();
            this.resetSubscription = null;
        }
    };
    Form.prototype.componentDidMount = function () {
        this.subscribe();
    };
    Form.prototype.componentDidUpdate = function (prevProps) {
        if (prevProps.form !== this.props.form) {
            this.unsubscribe();
            this.subscribe();
        }
    };
    Form.prototype.componentWillUnmount = function () {
        this.unsubscribe();
    };
    Form.prototype.render = function () {
        var _a = this.props, children = _a.children, _b = _a.layout, layout = _b === void 0 ? 'vertical' : _b, _c = _a.direction, direction = _c === void 0 ? 'column' : _c, className = _a.className, form = _a.form, onSubmit = _a.onSubmit, onSubmitFail = _a.onSubmitFail, onSubmitSuccess = _a.onSubmitSuccess, disableEnterSubmit = _a.disableEnterSubmit, _d = _a.disabled, disabled = _d === void 0 ? false : _d, scrollToError = _a.scrollToError, willScrollToError = _a.willScrollToError, props = __rest(_a, ["children", "layout", "direction", "className", "form", "onSubmit", "onSubmitFail", "onSubmitSuccess", "disableEnterSubmit", "disabled", "scrollToError", "willScrollToError"]);
        var childrenCtx = this.getChildrenContext(this.children);
        return (_jsx(Disabled, __assign({ value: disabled }, { children: _jsx(FormChildrenContext.Provider, __assign({ value: childrenCtx }, { children: _jsx(FormProvider, __assign({ value: form.ctx }, { children: _jsx("form", __assign({ ref: this.formRef }, props, { className: cx('zent-form-reactive', {
                            'zent-form-vertical': layout === 'vertical',
                            'zent-form-horizontal': layout === 'horizontal',
                            'zent-form-direction-row': direction === 'row',
                            'zent-form-direction-column': direction === 'column',
                        }, className), onSubmit: this.onSubmit, onReset: this.onReset, onKeyDown: this.onKeyDown, "data-zv": 'v10.0.11' }, { children: children }), void 0) }), void 0) }), void 0) }), void 0));
    };
    Form.displayName = 'ZentForm';
    Form.CombineErrors = CombineErrors;
    Form.useForm = useForm;
    Form.useField = useField;
    Form.useFieldArray = useFieldArray;
    Form.useFieldSet = useFieldSet;
    Form.useNamedChildModel = useNamedChildModel;
    Form.useFieldArrayChildModels = useFieldArrayChildModels;
    Form.useFieldArrayValue = useFieldArrayChildModels;
    Form.field = field;
    Form.set = set;
    Form.array = array;
    Form.form = form;
    Form.FieldValue = FieldValue;
    Form.FieldSetValue = FieldSetValue;
    Form.useFormValue = useFormValue;
    Form.useFieldValue = useFieldValue;
    Form.FieldValid = FieldValid;
    Form.useFormValid = useFormValid;
    Form.useFieldValid = useFieldValid;
    Form.useModelValue = useModelValue;
    Form.useModelValid = useModelValid;
    Form.ValidateOption = ValidateOption;
    Form.createAsyncValidator = createAsyncValidator;
    Form.isAsyncValidator = isAsyncValidator;
    Form.ValidateOccasion = ValidateOccasion;
    Form.TouchWhen = TouchWhen;
    return Form;
}(Component));
export { Form };
var FormValidationError = (function (_super) {
    __extends(FormValidationError, _super);
    function FormValidationError(message) {
        var _this = _super.call(this, message) || this;
        _this.name = 'FormValidationError';
        return _this;
    }
    return FormValidationError;
}(Error));
export { FormValidationError };
