"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FormContext = exports.FormContextStore = void 0;
const tslib_1 = require("tslib");
const FormDesignConstants_1 = require("./FormDesignConstants");
const AutoBind_1 = require("@appglue/common/dist/Common/AutoBind");
const DataUtilities_1 = require("@appglue/common/dist/Common/DataUtilities");
const RegisterUIControl_1 = require("./RegisterUIControl");
const XFormAndLayoutDesignPanel_1 = require("./XFormAndLayoutDesignPanel");
const ControlRenderContext_1 = require("./ControlRenderContext");
const StateManager_1 = require("@appglue/common/dist/Common/StateManager");
const IEditable_1 = require("../../CommonUI/IEditable");
const ExecutionData_1 = require("@appglue/common/dist/Common/Data/ExecutionData/ExecutionData");
const FormConstants_1 = require("./FormConstants");
// todo: make this a store
// todo: add other stuff here like data? hidden? disabled?
class FormContextStore {
    editContext;
    controlRenderContexts = {};
    otherRuntimeIssues = [];
    otherDesignIssues = [];
    constructor(editContext) {
        this.editContext = editContext;
    }
    getAllRuntimeIssues() {
        // add up all issues from all controls
        let issues = [];
        issues.push(...this.otherRuntimeIssues);
        for (let issueList of Object.values(this.controlRenderContexts)) {
            issues.push(...issueList.runtimeIssues);
        }
        return issues;
    }
    getAllDesignIssues() {
        // add up all issues from all controls
        let issues = [];
        issues.push(...this.otherDesignIssues);
        for (let issueList of Object.values(this.controlRenderContexts)) {
            issues.push(...issueList.designIssues);
        }
        return issues;
    }
    getControlRenderContext(control) {
        let retVal = this.controlRenderContexts[control.id];
        if (!retVal) {
            retVal = new ControlRenderContext_1.ControlRenderContext(control, this.editContext);
            this.controlRenderContexts[control.id] = retVal;
        }
        return retVal;
    }
    getControlRenderContextById(controlId, controls) {
        let retVal = this.controlRenderContexts[controlId];
        if (!retVal) {
            // find control based on id
            let control;
            for (let c of controls) {
                if (c.id === controlId) {
                    control = c;
                    break;
                }
            }
            if (control) {
                retVal = new ControlRenderContext_1.ControlRenderContext(control, this.editContext);
                this.controlRenderContexts[controlId] = retVal;
            }
            else {
                throw new Error("could not find control");
            }
        }
        return retVal;
    }
    updateValidationIssues(validations, controls, isRuntimeIssues) {
        // clear other issues
        if (isRuntimeIssues) {
            this.otherRuntimeIssues.length = 0;
        }
        else {
            this.otherDesignIssues.length = 0;
        }
        let controlIssues = {};
        for (let issue of validations) {
            if (issue.elementId) {
                let issues = controlIssues[issue.elementId];
                if (!issues) {
                    issues = [];
                    controlIssues[issue.elementId] = issues;
                }
                issues.push(issue);
                continue;
            }
            // do data name checking
            if (issue.dataName) {
                let controlId = null;
                for (let control of controls) {
                    let dataName = Reflect.get(control, "valueName");
                    if (dataName && dataName === issue.dataName) {
                        controlId = control.id;
                        break;
                    }
                }
                if (controlId) {
                    let issues = controlIssues[controlId];
                    if (!issues) {
                        issues = [];
                        controlIssues[controlId] = issues;
                    }
                    issues.push(issue);
                    continue;
                }
            }
            // not for a control
            if (isRuntimeIssues) {
                this.otherRuntimeIssues.push(issue);
            }
            else {
                this.otherDesignIssues.push(issue);
            }
        }
        // map to and ensure not over updating
        for (let control of controls) {
            let controlRenderContext = this.getControlRenderContext(control);
            let issues = controlIssues[controlRenderContext.control.id];
            if (isRuntimeIssues) {
                if (!issues && controlRenderContext.runtimeIssues.length !== 0) {
                    // clear the array
                    controlRenderContext.runtimeIssues.length = 0;
                }
                if (issues) {
                    // todo: we might want to update
                    controlRenderContext.runtimeIssues = issues;
                }
            }
            else {
                if (!issues && controlRenderContext.designIssues.length !== 0) {
                    // clear the array
                    controlRenderContext.designIssues.length = 0;
                }
                if (issues) {
                    // todo: we might want to update
                    controlRenderContext.designIssues = issues;
                }
            }
        }
    }
}
exports.FormContextStore = FormContextStore;
class FormContext {
    form;
    runtimeValidationProvider;
    data = new ExecutionData_1.ExecutionData();
    controlContexts;
    onFormDataChange;
    // callbacks
    onFormButtonClick;
    onFormCancelButton;
    onFormClose;
    completeOutcomeName;
    resultPromiseResolve;
    resultPromiseReject;
    resultPromise = new Promise((resolve, reject) => {
        if (this.completeOutcomeName) {
            resolve(this.completeOutcomeName);
        }
        else {
            this.resultPromiseResolve = resolve;
            this.resultPromiseReject = reject;
        }
    });
    formTitle;
    designer;
    formName;
    showControlBorders = true;
    expandedConfigPanel = true;
    isMovingConfigPanel = false;
    isShowEditLayer = false;
    selectedId = null;
    contextControl = null;
    designValidationProvider;
    onFormSave;
    sampleDataProvider;
    designConfig;
    clipboardElement = null;
    deleteControl = null;
    // called when we force a designer update
    onDesignerUpdate;
    topDesignerExtensions;
    bottomDesignerExtensions;
    // ------------------------------------------------
    // -- Designer Context ----------------------------
    // ------------------------------------------------
    eventLog = [];
    constructor(form) {
        this.form = form;
        this.controlContexts = new FormContextStore(this);
        this.computeDesignValidationIssues();
    }
    // runtime
    _runtimeHideControls = [];
    get runtimeHideControls() {
        return this._runtimeHideControls;
    }
    set runtimeHideControls(hideControlIds) {
        const previousHides = [...this._runtimeHideControls];
        const showControls = previousHides.filter((hide) => hideControlIds.indexOf(hide) < 0);
        const newlyHideControls = hideControlIds.filter((hide) => previousHides.indexOf(hide) < 0);
        const updateControls = [...showControls, ...newlyHideControls];
        this._runtimeHideControls = hideControlIds;
        StateManager_1.StateManager.propertiesChanged(this, updateControls);
    }
    _runtimeDisabledControls = [];
    get runtimeDisabledControls() {
        return this._runtimeDisabledControls;
    }
    set runtimeDisabledControls(disabledControlIds) {
        const previousHides = [...this._runtimeDisabledControls];
        const enabledControls = previousHides.filter((hide) => disabledControlIds.indexOf(hide) < 0);
        const newlyDisabledControls = disabledControlIds.filter((hide) => previousHides.indexOf(hide) < 0);
        const updateControls = [...enabledControls, ...newlyDisabledControls];
        this._runtimeDisabledControls = disabledControlIds;
        StateManager_1.StateManager.propertiesChanged(this, updateControls);
    }
    // this should ONLY be in designer.  Not here.
    _mode = FormDesignConstants_1.FormMode.FormDesign;
    get mode() {
        return this._mode;
    }
    set mode(value) {
        this._mode = value;
        if (value === FormDesignConstants_1.FormMode.Runtime)
            this.computeRuntimeValidations();
    }
    _lastSelectionElement = null;
    get lastSelectionElement() {
        return this._lastSelectionElement;
    }
    set lastSelectionElement(id) {
        this._lastSelectionElement = id;
    }
    completeFormWithOutcome(outcomename) {
        this.completeOutcomeName = outcomename;
        this.data.datalocked = true;
        if (this.resultPromiseResolve) {
            this.resultPromiseResolve(outcomename);
        }
        StateManager_1.StateManager.propertyChanged(this, "completeOutcomeName");
    }
    outcomeTriggered(name) {
        if (this.onFormButtonClick)
            this.onFormButtonClick(name, this.data);
        this.completeFormWithOutcome(name);
    }
    onClose() {
        if (this.onFormClose) {
            this.onFormClose(this.data);
        }
        this.completeFormWithOutcome(FormConstants_1.FormConstants.FORM_CLOSED_RESULT);
    }
    onCancel() {
        if (this.onFormCancelButton) {
            this.onFormCancelButton();
        }
        this.completeFormWithOutcome(FormConstants_1.FormConstants.FORM_CANCELED_RESULT);
    }
    getControlContext(control) {
        return this.controlContexts.getControlRenderContext(control);
    }
    computeRuntimeValidations(fieldName = "") {
        let breaks;
        breaks = [];
        for (let c of this.form.getAllControls()) {
            let results = c.getRuntimeValidationIssues();
            if (results) {
                breaks.push(...results);
            }
        }
        // call validation provider if it exists for additional validation
        let valBreaks = this.runtimeValidationProvider?.getRuntimeValidationIssues();
        if (valBreaks) {
            breaks.push(...valBreaks);
        }
        // todo: call built in rules
        if (fieldName) {
            const ruleBreaks = this.form.getRuleValidationIssues(fieldName);
            if (ruleBreaks) {
                breaks.push(...ruleBreaks);
            }
        }
        this.controlContexts.updateValidationIssues(breaks, this.form.getAllControls(), true);
        return breaks;
    }
    setFormDataValue(fieldName, value) {
        const currentValue = this.getFormDataValue(fieldName);
        if (value === currentValue)
            return;
        this.data.values = {
            ...this.data.values,
            [fieldName]: value,
        };
        // this.refreshUserForm();
        if (this.onFormDataChange) {
            this.onFormDataChange(this.data);
        }
        this.computeRuntimeValidations(fieldName);
        this.form.dataChangeByRule(fieldName);
        this.form.hideControlByRule();
        this.form.disableControlByRule();
        this.form.focusControlByRule();
        StateManager_1.StateManager.propertyChanged(this, fieldName);
        if (this.onFormDataChange) {
            this.onFormDataChange(this.data);
        }
    }
    getFormDataValue(fieldName) {
        if (!this.data.values)
            return;
        return this.data.values[fieldName];
    }
    setFormData(data) {
        if (data instanceof ExecutionData_1.ExecutionData) {
            this.data = data;
        }
        else {
            this.data.values = data;
        }
        this.computeRuntimeValidations();
    }
    getFormData() {
        return this.data;
    }
    cloneControl(control) {
        let type = Reflect.get(control, "__type");
        let registeredControl = RegisterUIControl_1.UIControlRegistration[type];
        let val = new registeredControl.prototype.constructor();
        return Object.assign(val, control);
    }
    onCopy(id) {
        let controlId = id ? id : this.lastSelectionElement;
        if (!controlId)
            return;
        let control = this.form.find(controlId);
        if (!control)
            return;
        this.clipboardElement = this.cloneControl(control);
        if (id)
            this.unselectContextControl();
        StateManager_1.StateManager.changed(this);
    }
    onCut(id) {
        let controlId = id ? id : this.lastSelectionElement;
        if (!controlId)
            return;
        let control = this.form.find(controlId);
        if (!control)
            return;
        this.clipboardElement = this.cloneControl(control);
        this.unSelectControl();
        this.form.remove(control);
        if (id)
            this.unselectContextControl();
        StateManager_1.StateManager.changed(this);
    }
    onDelete(id) {
        let controlId = id ? id : this.lastSelectionElement;
        if (!controlId)
            return;
        let control = this.form.find(controlId);
        if (!control)
            return;
        this.deleteControl = control;
        if (id)
            this.unselectContextControl();
        StateManager_1.StateManager.changed(this);
    }
    onPaste(id) {
        let controlId = id || this.lastSelectionElement;
        if (this.clipboardElement && controlId) {
            this.clipboardElement.id = DataUtilities_1.DataUtilities.generateUniqueId();
            let control = this.form.find(controlId);
            this.form.getContainers().forEach((c) => {
                let index = c.getControls().indexOf(control);
                if (index >= 0) {
                    c.add(this.cloneControl(this.clipboardElement), index + 1);
                }
            });
            if (id)
                this.unselectContextControl();
            StateManager_1.StateManager.changed(this);
        }
    }
    addToEventLog(event) {
        this.eventLog.push(event);
        // make sure does not grow too big
        if (this.eventLog.length > 100)
            this.eventLog.shift();
    }
    getEventLog() {
        return this.eventLog;
    }
    computeDesignValidationIssues() {
        let breaks;
        breaks = [];
        // value name is filled in (call data class)
        // value name cannot have special characters or space
        // buttons have names (call button class)
        for (let c of this.form.getContainers()) {
            let results = c.getDesignValidationIssues();
            if (results && results.length > 0) {
                breaks.push(...results);
            }
        }
        for (let c of this.form.getAllControls()) {
            let results = c.getDesignValidationIssues();
            if (results) {
                breaks.push(...results);
            }
        }
        // call validation provider if it exists for additional validation
        let valBreaks = this.designValidationProvider?.getDesignValidationIssues();
        if (valBreaks) {
            breaks.push(...valBreaks);
        }
        this.controlContexts.updateValidationIssues(breaks, this.form.getAllControls(), false);
        StateManager_1.StateManager.propertyChanged(this.controlContexts, "validationIssues");
        return breaks;
    }
    refreshDesigner() {
        this.designer?.forceUpdate();
        if (this.onDesignerUpdate) {
            this.onDesignerUpdate();
        }
        this.computeDesignValidationIssues();
    }
    getEditUI(defaultId) {
        const selectedId = this.selectedId || defaultId;
        if (!selectedId)
            return;
        if (selectedId === XFormAndLayoutDesignPanel_1.CONFIG_FORM_KEY) {
            return IEditable_1.EditableUtilities.getEditor(this.form);
        }
        let control = this.form.find(selectedId);
        if (control)
            return IEditable_1.EditableUtilities.getEditor(control);
    }
    getSelectedId() {
        return this.selectedId;
    }
    getSelectedControl(defaultControlId) {
        const selectedId = this.selectedId || defaultControlId;
        if (selectedId) {
            return this.form.find(selectedId);
        }
        return null;
    }
    selectControl(selectedId) {
        this.unSelectControl();
        this.selectedId = selectedId;
        this.lastSelectionElement = selectedId;
        if (selectedId !== XFormAndLayoutDesignPanel_1.CONFIG_FORM_KEY && this.form.find(selectedId)) {
            let cc = this.controlContexts.getControlRenderContextById(selectedId, this.form.getAllControls());
            cc.setSelected(true);
        }
        this.isShowEditLayer = true;
        StateManager_1.StateManager.changed(this);
    }
    unSelectControl() {
        if (this.selectedId) {
            if (this.selectedId !== XFormAndLayoutDesignPanel_1.CONFIG_FORM_KEY && this.form.find(this.selectedId)) {
                let cc = this.controlContexts.getControlRenderContextById(this.selectedId, this.form.getAllControls());
                cc.setSelected(false);
            }
        }
        this.selectedId = null;
        StateManager_1.StateManager.changed(this);
    }
    hideEditLayer() {
        this.isShowEditLayer = false;
        StateManager_1.StateManager.changed(this);
    }
    showEditLayer() {
        this.isShowEditLayer = true;
        StateManager_1.StateManager.changed(this);
    }
    toggleEditLayer() {
        this.isShowEditLayer = !this.isShowEditLayer;
        StateManager_1.StateManager.changed(this);
    }
    selectContextControl(control) {
        this.contextControl = control;
        StateManager_1.StateManager.changed(this);
    }
    unselectContextControl() {
        this.contextControl = null;
        StateManager_1.StateManager.changed(this);
    }
    refreshUserForm() {
        this.refreshDesigner();
        // todo: refresh user form
    }
}
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "outcomeTriggered", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "cloneControl", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "onCopy", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "onCut", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "onDelete", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "onPaste", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "refreshDesigner", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "selectControl", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "unSelectControl", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "hideEditLayer", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "showEditLayer", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "toggleEditLayer", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "selectContextControl", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "unselectContextControl", null);
tslib_1.__decorate([
    AutoBind_1.AutoBind
], FormContext.prototype, "refreshUserForm", null);
exports.FormContext = FormContext;
//# sourceMappingURL=FormContext.js.map