import { __decorate, __metadata } from "tslib";
import { FormMode } from "../FormDesignConstants";
import { AutoBind } from "../../Common/AutoBind";
import { UserFormData } from "../UserFormData";
import { XBaseControl } from "../Controls/XBaseControl";
import { DataUtilities } from "../../Common/DataUtilities";
import { UIControlRegistration } from "./RegisterUIControl";
import { CONFIG_FORM_KEY } from "./XFormAndLayoutDesignPanel";
import { ControlRenderContext } from "./ControlRenderContext";
import { StateManager } from "../../CommonUI/StateManagement/StateManager";
import { EditableUtilities } from "../../CommonUI/IEditable";
import { RuntimeContext } from "./RuntimeContext";
// todo: make this a store
// todo: add other stuff here like data? hidden? disabled?
export class FormContextStore {
    constructor(editContext) {
        this.controlRenderContexts = {};
        this.otherRuntimeIssues = [];
        this.otherDesignIssues = [];
        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(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(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;
                }
            }
        }
    }
}
export class FormContext {
    constructor(form) {
        this.data = new UserFormData();
        this.showControlBorders = true;
        this.expandedConfigPanel = true;
        this.isMovingConfigPanel = false;
        this.isShowEditLayer = false;
        // this should ONLY be in designer.  Not here.
        this._mode = FormMode.FormDesign;
        this.selectedId = null;
        this.contextControl = null;
        this.clipboardElement = null;
        this.deleteControl = null;
        this.eventLog = [];
        this._lastSelectionElement = null;
        this.form = form;
        this.controlContexts = new FormContextStore(this);
        this.runtimeContext = new RuntimeContext(this);
        this.computeDesignValidationIssues();
    }
    getControlContext(control) {
        return this.controlContexts.getControlRenderContext(control);
    }
    setFormDataValue(fieldName, value) {
        this.runtimeContext.setFormDataValue(fieldName, value);
        // this.refreshUserForm();
        if (this.onFormDataChange) {
            this.onFormDataChange(this.runtimeContext.data);
        }
    }
    getFormDataValue(fieldName) {
        return this.runtimeContext.getFormDataValue(fieldName);
    }
    setFormData(data) {
        this.runtimeContext.setFormData(data);
    }
    getFormData() {
        return this.runtimeContext.getFormData();
    }
    outcomeTriggered(name) {
        if (this.onFormButtonClick)
            this.onFormButtonClick(name, this.data);
    }
    cancelOutcomeTriggered(name) {
        if (this.onFormCancelButton)
            this.onFormCancelButton();
    }
    cloneControl(control) {
        let type = Reflect.get(control, "__type");
        let registeredControl = 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.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.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.changed(this);
    }
    onPaste(id) {
        let controlId = id || this.lastSelectionElement;
        if (this.clipboardElement && controlId) {
            this.clipboardElement.id = 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.changed(this);
        }
    }
    get mode() {
        return this._mode;
    }
    set mode(value) {
        this._mode = value;
        if (value === FormMode.Runtime)
            this.runtimeContext.computeRuntimeValidations();
    }
    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() {
        var _a;
        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.getAllControls()) {
            let results = c.getDesignValidationIssues();
            if (results) {
                breaks.push(...results);
            }
        }
        // call validation provider if it exists for additional validation
        let valBreaks = (_a = this.designValidationProvider) === null || _a === void 0 ? void 0 : _a.getDesignValidationIssues();
        if (valBreaks) {
            breaks.push(...valBreaks);
        }
        this.controlContexts.updateValidationIssues(breaks, this.form.getAllControls(), false);
        StateManager.propertyChanged(this.controlContexts, "validationIssues");
        return breaks;
    }
    refreshDesigner() {
        var _a;
        (_a = this.designer) === null || _a === void 0 ? void 0 : _a.forceUpdate();
        if (this.onDesignerUpdate) {
            this.onDesignerUpdate();
        }
        this.computeDesignValidationIssues();
    }
    getEditUI(defaultId) {
        const selectedId = this.selectedId || defaultId;
        if (!selectedId)
            return;
        if (selectedId === CONFIG_FORM_KEY) {
            return EditableUtilities.getEditor(this.form);
        }
        let control = this.form.find(selectedId);
        if (control)
            return EditableUtilities.getEditor(control);
    }
    getSelectedId() {
        return this.selectedId;
    }
    getSelectedControl(defaultControlId) {
        const selectedId = this.selectedId || defaultControlId;
        if (selectedId) {
            return this.form.find(selectedId);
        }
        return null;
    }
    set lastSelectionElement(id) {
        this._lastSelectionElement = id;
    }
    get lastSelectionElement() {
        return this._lastSelectionElement;
    }
    selectControl(selectedId) {
        this.unSelectControl();
        this.selectedId = selectedId;
        this.lastSelectionElement = selectedId;
        if (selectedId !== CONFIG_FORM_KEY && this.form.find(selectedId)) {
            let cc = this.controlContexts.getControlRenderContextById(selectedId, this.form.getAllControls());
            cc.setSelected(true);
        }
        this.isShowEditLayer = true;
        StateManager.changed(this);
    }
    unSelectControl() {
        if (this.selectedId) {
            if (this.selectedId !== 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.changed(this);
    }
    hideEditLayer() {
        this.isShowEditLayer = false;
        StateManager.changed(this);
    }
    showEditLayer() {
        this.isShowEditLayer = true;
        StateManager.changed(this);
    }
    toggleEditLayer() {
        this.isShowEditLayer = !this.isShowEditLayer;
        StateManager.changed(this);
    }
    selectContextControl(control) {
        this.contextControl = control;
        StateManager.changed(this);
    }
    unselectContextControl() {
        this.contextControl = null;
        StateManager.changed(this);
    }
    refreshUserForm() {
        this.refreshDesigner();
        // todo: refresh user form
    }
}
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "outcomeTriggered", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "cancelOutcomeTriggered", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [XBaseControl]),
    __metadata("design:returntype", XBaseControl)
], FormContext.prototype, "cloneControl", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "onCopy", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "onCut", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "onDelete", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "onPaste", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "refreshDesigner", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "selectControl", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "unSelectControl", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "hideEditLayer", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "showEditLayer", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "toggleEditLayer", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "selectContextControl", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "unselectContextControl", null);
__decorate([
    AutoBind,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FormContext.prototype, "refreshUserForm", null);
//# sourceMappingURL=FormContext.js.map