import { __decorate } from "tslib";
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable no-plusplus */
/* eslint-disable no-bitwise */
/* eslint-disable class-methods-use-this */
/**
@license
Copyright 2018 The Advanced REST client authors <arc@mulesoft.com>
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/
import { html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js';
import { assignTheme } from '../../monaco/theme.js';
import { detectLanguage } from '../../monaco/helper.js';
import { ScrollHelper } from '../../ui/lib/ScrollHelper.js';
import { Events } from '../../events/Events.js';
import ApiElement from '../ApiElement.js';
/* global monaco */
let modelId = 0;
export default class BodyTextEditor extends ApiElement {
    /**
     * A HTTP body.
     * @attribute
     */
    get value() {
        return this.valueInternal;
    }
    set value(value) {
        const old = this.valueInternal;
        if (old === value) {
            return;
        }
        this.valueInternal = value || '';
        const { editor: e } = this;
        if (e) {
            e.setValue(value || '');
        }
    }
    /**
     * The current language of the editor. It is set via the `contentType`.
     */
    get language() {
        return this.languageInternal;
    }
    /**
     * Uses the current content type to detect language support.
     * @attribute
     */
    get contentType() {
        return this.contentTypeInternal;
    }
    set contentType(value) {
        const old = this.contentTypeInternal;
        if (old === value) {
            return;
        }
        this.contentTypeInternal = value;
        const oldLang = this.languageInternal;
        const lang = detectLanguage(value);
        if (oldLang === lang) {
            return;
        }
        this.languageInternal = lang;
        this.setLanguage(lang);
    }
    /**
     * When set the editor is in read only mode.
     * @attribute
     */
    get readOnly() {
        return this.readOnlyValue;
    }
    set readOnly(value) {
        const old = this.readOnlyValue;
        if (old === value) {
            return;
        }
        this.readOnlyValue = value;
        this.setEditorConfigProperty('readOnly', value);
    }
    get editor() {
        return this.monacoInstance;
    }
    get schemas() {
        return this.validationSchemas;
    }
    set schemas(value) {
        const old = this.validationSchemas;
        if (old === value) {
            return;
        }
        this.validationSchemas = value;
        this.updateEditorSchemas(value);
    }
    constructor() {
        super();
        this.valueInternal = '';
        this.readOnlyValue = false;
        this.valueChanged = this.valueChanged.bind(this);
        this.observer = new ResizeObserver(this.handleObservedResize.bind(this));
    }
    connectedCallback() {
        super.connectedCallback();
        ScrollHelper.addListeners(this, this.resizeHandler.bind(this));
        const { container } = this;
        if (container) {
            this.observer.observe(container);
        }
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        ScrollHelper.removeListeners(this);
        this.editor?.dispose();
        const { container } = this;
        if (container) {
            this.observer.unobserve(container);
        }
    }
    firstUpdated() {
        const config = this.generateEditorConfig();
        const node = this.container;
        // @ts-ignore
        const instance = monaco.editor.create(node, config);
        // instance.onDidChangeModelContent(this.valueChanged);
        // We use `onDidBlurEditorText` which is the closest we can get to an input's 
        // `change` event. This will reduce the number of updates on the parent 
        // editor.
        instance.onDidBlurEditorText(this.valueChanged);
        this.monacoInstance = instance;
        this.setupActions(instance);
        this.observer.observe(node);
    }
    /**
     * Handler for the `resize` event provided by the resizable mixin.
     */
    resizeHandler() {
        if (this.refreshDebouncer) {
            clearTimeout(this.refreshDebouncer);
        }
        this.refreshDebouncer = setTimeout(this.refreshEditor.bind(this));
    }
    /**
     * A function that is called from the `[resizeHandler]` in a timeout to reduce the number
     * of computations during the application initialization, where it can receive hundreds of
     * the `resize` events.
     */
    refreshEditor() {
        this.refreshDebouncer = undefined;
        if (!this.monacoInstance) {
            return;
        }
        this.monacoInstance.layout();
    }
    /**
     * @param lang New language to set
     */
    setLanguage(lang) {
        const { editor: e } = this;
        if (!e || !lang) {
            return;
        }
        const model = e.getModel();
        if (model) {
            // @ts-ignore
            monaco.editor.setModelLanguage(model, lang);
        }
    }
    /**
     * Sets up editor actions
     */
    setupActions(inst) {
        inst.addAction({
            id: 'send-http-request',
            label: 'Send request',
            keybindings: [
                // @ts-ignore
                monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
            ],
            precondition: undefined,
            keybindingContext: undefined,
            contextMenuGroupId: 'navigation',
            contextMenuOrder: 1.5,
            run: () => {
                Events.Http.Request.send(this);
            }
        });
    }
    valueChanged() {
        const { editor: ed } = this;
        if (!ed) {
            return;
        }
        const editorValue = ed.getValue();
        if (editorValue === this.valueInternal) {
            return;
        }
        this.valueInternal = editorValue;
        if (this.changeTimeout) {
            // window.clearTimeout(this[changeTimeout]);
            window.cancelAnimationFrame(this.changeTimeout);
        }
        this.changeTimeout = window.requestAnimationFrame(() => {
            this.notifyChange();
        });
    }
    /**
     * Generates Monaco configuration
     */
    generateEditorConfig() {
        const { value = '', readOnly, languageInternal: language } = this;
        // @ts-ignore
        this.modelUri = monaco.Uri.parse(`http://raw-editor/model${++modelId}.json`);
        // @ts-ignore
        const model = monaco.editor.createModel(value, language || 'json', this.modelUri);
        const schemas = this.getCurrentSchemas();
        this.updateEditorSchemas(schemas);
        let config = {
            minimap: {
                enabled: false,
            },
            readOnly,
            formatOnType: true,
            folding: true,
            tabSize: 2,
            detectIndentation: true,
            // value,
            // automaticLayout: true,
            model,
            padding: { top: 0, bottom: 0 },
            scrollBeyondLastLine: false,
            foldingHighlight: true,
            selectionHighlight: true,
        };
        // @ts-ignore
        config = assignTheme(monaco, config);
        if (language) {
            config.language = language;
        }
        return config;
    }
    getCurrentSchemas() {
        const { schemas } = this;
        if (Array.isArray(schemas) && schemas.length) {
            schemas[0].fileMatch = [this.modelUri?.toString() || ''];
            return schemas;
        }
        return [this.createDefaultSchema()];
    }
    createDefaultSchema() {
        const schema = {
            uri: "http://raw-editor/default-schema.json",
            fileMatch: [this.modelUri?.toString() || ''],
            schema: {},
        };
        return schema;
    }
    updateEditorSchemas(schemas) {
        if (!this.modelUri || !schemas) {
            // the editor is not yet ready. This will be called again when it is ready.
            return;
        }
        let value = schemas;
        if (!Array.isArray(value) || !value.length) {
            value = [this.createDefaultSchema()];
        }
        else {
            value[0] = { ...value[0], fileMatch: [this.modelUri.toString()] };
        }
        // @ts-ignore
        monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
            validate: true,
            schemas: value,
        });
    }
    /**
     * @param prop The property to set
     */
    setEditorConfigProperty(prop, value) {
        const { editor: e } = this;
        if (!e) {
            return;
        }
        const opts = {
            [prop]: value,
        };
        e.updateOptions(opts);
    }
    handleObservedResize(entries) {
        const { editor: e } = this;
        if (!e) {
            return;
        }
        const [entry] = entries;
        const rect = entry.contentRect;
        e.layout({ height: rect.height, width: rect.width });
    }
    render() {
        return html `
      <div id="container"></div>
      ${this.renderFormInfo()}
    `;
    }
    renderFormInfo() {
        if (!this.allowFormInfo) {
            return nothing;
        }
        return html `
    <p class="form-info">
      Note that the content-type header will be added when the request is sent. 
      We encourage you to use variables to insert sensitive data.
    </p>
    `;
    }
}
__decorate([
    query('#container')
], BodyTextEditor.prototype, "container", void 0);
__decorate([
    property({ type: String })
], BodyTextEditor.prototype, "value", null);
__decorate([
    property({ type: String, reflect: true })
], BodyTextEditor.prototype, "contentType", null);
__decorate([
    property({ type: Boolean, reflect: true })
], BodyTextEditor.prototype, "readOnly", null);
__decorate([
    property({ type: Boolean })
], BodyTextEditor.prototype, "allowFormInfo", void 0);
//# sourceMappingURL=BodyTextEditor.js.map