import { __decorate } from "tslib";
/* eslint-disable class-methods-use-this */
/**
@license
Copyright 2020 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 } from 'lit/decorators.js';
import { live } from 'lit/directives/live.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { Property } from '@api-client/core/build/browser.js';
import { decodeUrlEncoded, encodeUrlEncoded, createViewModel, formArrayToString, } from './UrlEncodeUtils.js';
import ApiElement from '../ApiElement.js';
import '../../define/ui/ui-icon.js';
import '../../define/ui/ui-icon-button.js';
import '../../define/ui/ui-switch.js';
/**
 * A body editor specializing in URL encoded data.
 */
export default class BodyUrlEncodedEditor extends ApiElement {
    constructor() {
        super(...arguments);
        /**
         * When set the editor is in read only mode.
         * @attribute
         */
        this.readOnly = false;
        /**
         * When set all controls are disabled in the form
         * @attribute
         */
        this.disabled = false;
        /**
         * When set it automatically encodes and decodes values.
         * @attribute
         */
        this.autoEncode = false;
        this.valueInternal = '';
        this.modelInternal = [];
    }
    /**
     * The HTTP body.
     */
    get value() {
        return this.valueInternal;
    }
    set value(value) {
        const old = this.valueInternal;
        if (old === value) {
            return;
        }
        this.valueInternal = value || '';
        this.setupModel(value || '');
    }
    /**
     * Computed data model for the view.
     * Don't set both `value` and `model`. If the model exists then
     * set only it or otherwise the `value` setter override the model.
     */
    get model() {
        return this.modelInternal;
    }
    set model(value) {
        const old = this.modelInternal;
        if (old === value) {
            return;
        }
        if (Array.isArray(value)) {
            this.modelInternal = value;
        }
        else {
            this.modelInternal = [];
        }
        this.valueInternal = this.modelToValue();
        this.requestUpdate();
    }
    update(cp) {
        if (cp.has('autoEncode')) {
            this.updateValue();
        }
        super.update(cp);
    }
    /**
     * Removes all parameters.
     */
    clear() {
        this.model = [];
        this.updateValue();
        this.requestUpdate();
    }
    /**
     * Removes a parameter from an index.
     */
    removeProperty(index) {
        const items = this.model;
        items.splice(index, 1);
        this.updateValue();
        this.requestUpdate();
    }
    /**
     * Toggles enabled state of a parameter.
     */
    toggleProperty(index, enabled) {
        const items = this.model;
        const item = items[index];
        item.enabled = enabled;
        this.updateValue();
    }
    /**
     * Updates name/value of a parameter on a specific index.
     */
    updateProperty(index, field, value) {
        const item = this.model[index];
        const old = item[field];
        if (old === value) {
            return;
        }
        item[field] = value;
        this.updateValue();
    }
    addParameter(name = '', value = '') {
        if (this.readOnly || this.disabled) {
            return;
        }
        const prop = Property.String(name, value, true);
        const items = this.model;
        items[items.length] = prop.toJSON();
        this.model = items;
        this.requestUpdate();
        this.focusLast();
    }
    updateValue() {
        if (this.readOnly) {
            return;
        }
        this.valueInternal = this.modelToValue();
        this.notifyChange();
    }
    modelToValue() {
        const { model, autoEncode } = this;
        if (autoEncode) {
            const encoded = encodeUrlEncoded(model);
            return formArrayToString(encoded);
        }
        return formArrayToString(this.model);
    }
    /**
     * Called when the `value` property change. It generates the view model
     * for the editor.
     */
    setupModel(value) {
        if (!value) {
            this.model = [];
            return;
        }
        const model = createViewModel(value);
        if (this.autoEncode) {
            this.modelInternal = decodeUrlEncoded(model);
        }
        else {
            this.modelInternal = model;
        }
        this.requestUpdate();
    }
    async focusLast() {
        await this.updateComplete;
        const row = this.shadowRoot?.querySelector('.param-row:last-of-type');
        if (!row) {
            return;
        }
        try {
            const node = row.querySelector('.name-input input');
            if (node) {
                node.focus();
            }
        }
        catch (e) {
            // ...
        }
    }
    /**
     * Encodes current parameters in the model, updated the value, and notifies the change
     */
    encodeParameters() {
        const encoded = encodeUrlEncoded(this.model);
        this.modelInternal = encoded;
        this.valueInternal = formArrayToString(encoded);
        this.notifyChange();
        this.requestUpdate();
    }
    /**
     * Encodes current parameters in the model, updated the value, and notifies the change
     */
    decodeParameters() {
        const decoded = decodeUrlEncoded(this.model);
        this.modelInternal = decoded;
        this.valueInternal = formArrayToString(decoded);
        this.notifyChange();
        this.requestUpdate();
    }
    handleAdd() {
        this.addParameter();
    }
    handleToggle(e) {
        const node = e.currentTarget;
        const index = Number(node.dataset.index);
        if (Number.isNaN(index)) {
            return;
        }
        this.toggleProperty(index, node.checked);
    }
    handleRemove(e) {
        const node = e.currentTarget;
        const index = Number(node.dataset.index);
        if (Number.isNaN(index)) {
            return;
        }
        this.removeProperty(index);
    }
    handleNameChange(e) {
        const input = e.target;
        const index = Number(input.dataset.index);
        if (Number.isNaN(index)) {
            return;
        }
        this.updateProperty(index, 'name', input.value);
        input.checkValidity();
    }
    handleValueChange(e) {
        const input = e.target;
        const index = Number(input.dataset.index);
        if (Number.isNaN(index)) {
            return;
        }
        this.updateProperty(index, 'value', input.value);
        input.checkValidity();
    }
    render() {
        const items = this.model;
        if (!Array.isArray(items) || !items.length) {
            return html `${this.renderEmptyMessage()}${this.renderFormInfo()}`;
        }
        return this.renderParams(items);
    }
    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>
    `;
    }
    renderEmptyMessage() {
        return html `
    <div class="empty-message">No parameters added</div>
    `;
    }
    renderParams(items) {
        return html `
    ${items.map((item, index) => this.renderParam(item, index))}
    ${this.renderFormInfo()}
    `;
    }
    renderParam(item, index) {
        return html `
    <div class="param-row">
      ${this.renderToggleButton(item, index)}
      ${this.renderNameField(item, index)}
      ${this.renderValueField(item, index)}
      ${this.renderDeleteParameter(index)}
    </div>
    `;
    }
    renderToggleButton(item, index) {
        return html `
    <ui-switch 
      .checked="${!!item.enabled}" 
      @change="${this.handleToggle}" 
      title="Enable / disable parameter"
      aria-label="Activate to toggle enabled state of this parameter"
      data-index="${index}"
      ?disabled="${this.readOnly}"
    ></ui-switch>
    `;
    }
    renderDeleteParameter(index) {
        return html `
    <ui-icon-button 
      title="Removes this parameter"
      aria-label="Deletes this parameter" 
      data-index="${index}" 
      @click="${this.handleRemove}"
      ?disabled="${this.readOnly}"
    >
      <ui-icon icon="deleteOutline" role="presentation"></ui-icon>
    </ui-icon-button>
    `;
    }
    renderNameField(item, index) {
        const { autoEncode } = this;
        const pattern = !autoEncode ? '\\S*' : undefined;
        return html `
    <div class="input-wrapper name-input">
      <div class="error" role="presentation">Invalid character</div>
      <div class="state"></div>
      <input
        type="text"
        name="name"
        autocomplete="off"
        .value="${live(item.name)}" 
        data-index="${index}" 
        ?readOnly="${this.readOnly}" 
        pattern="${ifDefined(pattern)}"
        aria-label="Parameter name value"
        @change="${this.handleNameChange}"
      />
    </div>
    `;
    }
    renderValueField(item, index) {
        const { autoEncode } = this;
        const pattern = !autoEncode ? '\\S*' : undefined;
        return html `
    <div class="input-wrapper value-input">
      <div class="error" role="presentation">Invalid character</div>
      <div class="state"></div>
      <input
        type="text"
        name="value"
        .value="${live(item.value)}" 
        data-index="${index}" 
        ?readOnly="${this.readOnly}" 
        pattern="${ifDefined(pattern)}"
        aria-label="Parameter value"
        @change="${this.handleValueChange}"
      />
    </div>
    `;
    }
}
__decorate([
    property({ type: Boolean, reflect: true })
], BodyUrlEncodedEditor.prototype, "readOnly", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], BodyUrlEncodedEditor.prototype, "disabled", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], BodyUrlEncodedEditor.prototype, "autoEncode", void 0);
__decorate([
    property({ type: Boolean })
], BodyUrlEncodedEditor.prototype, "allowFormInfo", void 0);
__decorate([
    property({ type: String })
], BodyUrlEncodedEditor.prototype, "value", null);
//# sourceMappingURL=BodyUrlEncodedEditor.js.map