import { h } from '@stencil/core';
import formToObj from 'form-to-obj';
import validate from 'validate.js';
// import populate from 'populate.js';
import { TranslateService } from "@ema/core";
export class Form {
    constructor() {
        this.formClass = 'form-horizontal col-12';
        this.resettable = false;
        this.submitLabel = 'SUBMIT';
        this.translateService = TranslateService.instance;
    }
    componentDidLoad() {
        this.postRender();
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        this.form = this.el.getElementsByTagName('form')[0];
        if (this.entity) {
            this.entityForm = this.el.getElementsByTagName('ema-entity-form')[0];
            this.entityForm.setData(this.entity);
            if (this.definitions) {
                this.entityForm.setDefinitions(this.definitions);
            }
        }
        this.initValidations();
    }
    async handleSubmit(e) {
        let errors;
        if (this.constraints) {
            errors = validate(this.form, this.constraints);
            this.showErrors(this.form, errors || {});
        }
        if (errors)
            this.logErrors(errors);
        if (!this.action) {
            e.preventDefault();
        }
        if (this.action && errors) {
            e.preventDefault();
        }
        if (!errors) {
            let obj = formToObj(this.form);
            if (this.submitHandler) {
                obj = await this.submitHandler(obj, this.dataType);
            }
            if (obj) {
                this.formSubmitHandler();
            }
            else {
                console.error('form submit failure');
            }
        }
    }
    formSubmitHandler() {
        this.formSubmit.emit({ form: this.form, data: formToObj(this.form) });
    }
    async setData(data) {
        if (!data.data) {
            await this.setEntity(data);
        }
        else {
            await this.setEntity(data.data);
        }
    }
    async setEntity(entity) {
        if (entity.meta && entity.meta.definitions) {
            await this.setDefinitions(entity.meta.definitions);
        }
        this.entity = entity;
    }
    async setDefinitions(definitions) {
        typeof definitions === 'string' ? this.definitions = JSON.parse(definitions) : this.definitions = definitions;
        let constraints = {};
        Object.keys(this.definitions).forEach((k, i) => {
            let field = this.definitions[k];
            if (field.constraints) {
                constraints[k] = field.constraints;
            }
        });
        this.setConstraints(constraints);
    }
    async setConstraints(constraints) {
        typeof constraints === 'string' ? this.constraints = JSON.parse(constraints) : this.constraints = constraints;
    }
    async setSubmitInterceptor(submitFn) {
        this.submitHandler = submitFn;
    }
    async setTranslateService(instance) {
        this.translateService = instance;
        this.update();
    }
    async update() {
        this.render();
    }
    initValidations() {
        setTimeout(() => {
            const self = this;
            let inputs = this.el.querySelectorAll("input, textarea, select");
            for (let i = 0; i < inputs.length; ++i) {
                inputs.item(i).addEventListener("change", function (ev) {
                    let errors = validate(self.form, self.constraints) || {};
                    self.showErrorsForInput(this, errors[this['getAttribute']('name')]);
                });
            }
            // data validation: requires moment.js
            validate.extend(validate.validators.datetime, {
                // The value is guaranteed not to be null or undefined but otherwise it could be anything.
                parse: function (value, options) {
                    return +moment.utc(value);
                },
                // Input is a unix timestamp
                format: function (value, options) {
                    var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss";
                    return moment.utc(value).format(format);
                }
            });
            // fixme:
            validate.validators.presence.message = '^' + this.translate('FIELD REQUIRED');
        }, 20);
    }
    showErrors(form, errors) {
        // Loop through all the inputs and show the errors for that input
        Array.from(form.querySelectorAll("input[name], select[name]")).forEach((input) => {
            // Since the errors can be null if no errors were found we need to handle that
            this.showErrorsForInput(input, errors && errors[input['getAttribute']('name')]);
        });
    }
    showErrorsForInput(input, errors) {
        // This is the root of the input: Find where the error messages will be insert into
        let formGroup = this.closestParent(input.parentNode, "form-group");
        if (formGroup) {
            let messages = formGroup.querySelector(".messages");
            // First we remove any old messages and resets the classes
            this.resetFormGroup(formGroup);
            // If we have errors
            if (errors) {
                // we first mark the group has having errors
                formGroup.classList.add("has-error");
                // then we append all the errors
                Object.keys(errors).forEach((k, i) => {
                    this.addError(messages, errors[k]);
                });
            }
            else {
                // otherwise we simply mark it as success
                formGroup.classList.add("has-success");
            }
        }
        else {
            if (errors) {
                this.logErrors(errors, input['getAttribute']('name'));
            }
        }
    }
    logErrors(errors, fieldName = undefined) {
        Object.keys(errors).forEach((k, i) => {
            if (fieldName) {
                console.error(fieldName + ': ' + errors[k]);
            }
            else {
                console.error(k + ': ' + errors[k]);
            }
        });
    }
    closestParent(child, className) {
        // Recusively finds the closest parent that has the specified class
        if (!child || child == document) {
            return null;
        }
        if (child.classList.contains(className)) {
            return child;
        }
        else {
            return this.closestParent(child.parentNode, className);
        }
    }
    resetFormGroup(formGroup) {
        // Remove the success and error classes
        formGroup.classList.remove("has-error");
        formGroup.classList.remove("has-success");
        // and remove any old messages
        Array.from(formGroup.querySelectorAll(".help-block.error")).forEach((el) => {
            el.parentNode.removeChild(el);
        });
    }
    // Adds the specified error with the following markup: <p class="help-block error">[message]</p>
    addError(messages, error) {
        let block = document.createElement("p");
        block.classList.add("help-block");
        block.classList.add("error");
        block.innerText = this.translate(error);
        messages.appendChild(block);
    }
    translate(str) {
        return this.translateService.get(str);
    }
    render() {
        if (this.dataJson) {
            this.setData(JSON.parse(this.dataJson));
        }
        if (this.entityJson) {
            this.setEntity(JSON.parse(this.entityJson));
        }
        if (this.definitionsJson) {
            this.setDefinitions(JSON.parse(this.definitionsJson));
        }
        if (this.language && this.language !== this.translateService.language) {
            this.translateService.use(this.language);
        }
        return (h("form", { onSubmit: (e) => this.handleSubmit(e), class: this.formClass, role: 'form', action: this.action || 'javascript:;', method: this.method || 'get' },
            this.entity &&
                h("div", null,
                    h("ema-entity-form", null)),
            h("slot", null),
            this.entity &&
                h("div", { class: "form-group row" },
                    h("div", { class: "col-md-8 offset-md-4" },
                        h("button", { type: "submit", class: "btn btn-primary mr-2" }, this.translate(this.submitLabel)),
                        this.resettable &&
                            h("button", { type: "reset", class: "btn btn-primary" }, this.translate('RESET')),
                        this.dataType &&
                            h("ema-permission", { can: '["delete"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-danger mr-2 delete-btn" }, this.translate('DELETE'))),
                        this.dataType &&
                            h("ema-permission", { can: '["create"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-secondary mr-2 delete-create" }, this.translate('CREATE')))),
                    this.submitError &&
                        h("div", { class: "col-md-8 offset-md-4 messages help-block error" },
                            this.translate(this.submitError),
                            "."))));
    }
    static get is() { return "ema-form"; }
    static get originalStyleUrls() { return {
        "$": ["form.scss"]
    }; }
    static get styleUrls() { return {
        "$": ["form.css"]
    }; }
    static get properties() { return {
        "action": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "action",
            "reflect": false
        },
        "dataJson": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "data-json",
            "reflect": false
        },
        "dataType": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "data-type",
            "reflect": false
        },
        "definitionsJson": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "definitions-json",
            "reflect": false
        },
        "entityJson": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "entity-json",
            "reflect": false
        },
        "formClass": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "form-class",
            "reflect": false,
            "defaultValue": "'form-horizontal col-12'"
        },
        "language": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "language",
            "reflect": false
        },
        "method": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "method",
            "reflect": false
        },
        "resettable": {
            "type": "boolean",
            "mutable": false,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "resettable",
            "reflect": false,
            "defaultValue": "false"
        },
        "submitError": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "submit-error",
            "reflect": false
        },
        "submitLabel": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "submit-label",
            "reflect": false,
            "defaultValue": "'SUBMIT'"
        }
    }; }
    static get states() { return {
        "entity": {}
    }; }
    static get events() { return [{
            "method": "formSubmit",
            "name": "formSubmit",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": ""
            },
            "complexType": {
                "original": "any",
                "resolved": "any",
                "references": {}
            }
        }]; }
    static get methods() { return {
        "setData": {
            "complexType": {
                "signature": "(data: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "setEntity": {
            "complexType": {
                "signature": "(entity: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "setDefinitions": {
            "complexType": {
                "signature": "(definitions: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "setConstraints": {
            "complexType": {
                "signature": "(constraints: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "setSubmitInterceptor": {
            "complexType": {
                "signature": "(submitFn: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "setTranslateService": {
            "complexType": {
                "signature": "(instance: any) => Promise<void>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "update": {
            "complexType": {
                "signature": "() => Promise<void>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<void>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        }
    }; }
    static get elementRef() { return "el"; }
}
