{% import '_includes/forms.twig' as forms %}
{% import 'codeeditor/codeEditor.twig' as codeEditor %}

{% set readOnly = readOnly ?? false %}

{% set baseMonacoOptions = {
  tabSize: 2,
  showUnused: false,
} %}

{% if readOnly %}
  {% set baseMonacoOptions = baseMonacoOptions|merge({
    domReadOnly: true,
    readOnly: true
  }) %}
{% endif %}

{% set wrapperClasses = ['monaco-editor-background-frame'] %}
{% set baseCodeEditorOptions = {} %}

{% embed '_includes/forms/field.twig' with {
  label: 'Toolbar'|t('ckeditor'),
  instructions: 'Drag toolbar items into the editor.'|t('ckeditor'),
  id: 'toolbar',
  field,
  errors: field.getErrors('toolbar'),
  data: {'error-key': 'toolbar'},
} %}
  {% block input %}
    <div id="toolbar-builder" class="ckeditor-tb pane{{ readOnly ? ' disabled' : '' }}" data-available-items="{{ toolbarItems|json_encode|e('html_attr') }}">
      <div class="ck ck-reset_all">
        <div class="ckeditor-tb--items ckeditor-tb--source ck ck-reset ck-editor ck-rounded-corners">
          <div class="ck ck-toolbar">
            <div class="ck ck-toolbar__items"></div>
          </div>
        </div>

        <div class="ckeditor-tb--items ckeditor-tb--target ck ck-reset ck-editor ck-rounded-corners">
          <div class="ck ck-editor__top">
            <div class="ck ck-toolbar">
              <div class="ck ck-toolbar__items"></div>
            </div>
          </div>
          <div class="ck ck-editor__main">
            <div class="ck ck-content ck-editor__editable ck-editor__editable_inline"></div>
          </div>
        </div>
      </div>

      {{ hiddenInput('toolbar', field.toolbar|json_encode) }}
    </div>
  {% endblock %}
{% endembed %}

{{ forms.field({
  label: 'Entry Types'|t('app'),
  instructions: 'Choose the types of entries that can be created in this field.'|t('app'),
  id: 'entry-types',
  name: 'entryTypes[]',
  values: field.getEntryTypes(),
  allowOverrides: true,
  create: true,
  sortable: true,
  errors: field.getErrors('entryTypes'),
  data: {'error-key': 'entryTypes'},
  disabled: readOnly,
}, 'template:ckeditor/_entry-type-select.twig') }}

{{ forms.checkboxSelectField({
  label: 'Heading Levels'|t('ckeditor'),
  instructions: 'Choose which heading levels should be available to this field.'|t('ckeditor'),
  id: 'heading-levels',
  name: 'headingLevels',
  options: [
    {label: 'Heading {level}'|t('ckeditor', {level: 1}), value: '1'},
    {label: 'Heading {level}'|t('ckeditor', {level: 2}), value: '2'},
    {label: 'Heading {level}'|t('ckeditor', {level: 3}), value: '3'},
    {label: 'Heading {level}'|t('ckeditor', {level: 4}), value: '4'},
    {label: 'Heading {level}'|t('ckeditor', {level: 5}), value: '5'},
    {label: 'Heading {level}'|t('ckeditor', {level: 6}), value: '6'},
  ],
  values: field.headingLevels,
  disabled: readOnly,
}) }}

{{ forms.checkboxSelectField({
  label: 'Advanced Link Fields'|t('ckeditor'),
  id: 'advanced-link-fields',
  name: 'advancedLinkFields',
  showAllOption: false,
  options: advanceLinkOptions,
  values: field.advancedLinkFields,
  sortable: true,
  disabled: readOnly,
}) }}

{% embed '_includes/forms/field' with {
  label: 'Field Limit'|t('app'),
  instructions: "The maximum number of words or characters the field is allowed to have."|t('ckeditor'),
  id: 'field-limit',
  errors: field.getErrors(field.wordLimit ? 'wordLimit' : 'characterLimit')
} %}
  {% import "_includes/forms" as forms %}
  {% block input %}
    <div class="flex">
      {{ forms.text({
        id: 'fieldLimit',
        name: 'fieldLimit',
        value: field.wordLimit ?? field.characterLimit,
        size: 3,
        disabled: readOnly,
      }) }}
      {{ forms.select({
        id: 'limitUnit',
        name: 'limitUnit',
        options: [
          { value: 'words', label: 'Words'|t('ckeditor') },
          { value: 'chars', label: 'Characters'|t('app') },
        ],
        value: field.characterLimit ? 'chars' : 'words',
        disabled: readOnly,
      }) }}
    </div>
  {% endblock %}
{% endembed %}

{{ forms.lightswitchField({
  id: 'show-word-count',
  name: 'showWordCount',
  label: 'Show word count'|t('ckeditor'),
  on: field.showWordCount,
  disabled: readOnly,
}) }}

<hr>

<a class="fieldtoggle" data-target="assets">{{ 'Assets'|t('app') }}</a>
<div id="assets" class="hidden">
  {{ forms.checkboxSelectField({
    id: 'availableVolumes',
    name: 'availableVolumes',
    label: 'Available Volumes'|t('ckeditor'),
    instructions: 'The volumes that should be available when selecting assets.'|t('ckeditor'),
    options: volumeOptions,
    values: field.availableVolumes,
    showAllOption: volumeOptions|length ? true : false,
    disabled: readOnly,
  }) }}

  {{ forms.lightswitchField({
    label: 'Show unpermitted volumes'|t('ckeditor'),
    instructions: 'Whether to show volumes that the user doesn’t have permission to view.'|t('ckeditor'),
    id: 'showUnpermittedVolumes',
    name: 'showUnpermittedVolumes',
    on: field.showUnpermittedVolumes,
    disabled: readOnly,
  }) }}

  {{ forms.lightswitchField({
    label: 'Show unpermitted files'|t('ckeditor'),
    instructions: 'Whether to show files that the user doesn’t have permission to view, per the “View files uploaded by other users” permission.'|t('ckeditor'),
    id: 'showUnpermittedFiles',
    name: 'showUnpermittedFiles',
    on: field.showUnpermittedFiles,
    disabled: readOnly,
  }) }}

  {% embed '_includes/forms/field.twig' with {
    label: 'Image Mode'|t('ckeditor'),
    instructions: 'How should new images be added to the field contents?'|t('ckeditor'),
  } %}
    {% block input %}
      <div id="mode" class="flex items-start gap-l">
        <label class="nowrap">
          <img class="mb-xs" src="{{ baseIconsUrl }}/img.svg" width="80" height="60" alt="">
          {{ input('radio', 'imageMode', 'img', {
            checked: field.imageMode == 'img',
            class: 'fieldtoggle',
            disabled: readOnly,
            data: {'target-prefix': 'image-mode--'},
          }) }}
          {{ 'Image tags'|t('ckeditor') }}
        </label>
        <label class="nowrap">
          <img class="mb-xs" src="{{ baseIconsUrl }}/entries.svg" width="80" height="60" alt="">
          {{ input('radio', 'imageMode', 'entries', {
            checked: field.imageMode == 'entries',
            class: 'fieldtoggle',
            disabled: readOnly,
            data: {'target-prefix': 'image-mode--'},
          }) }}
          {{ 'Nested entries'|t('ckeditor') }}
        </label>
      </div>
    {% endblock %}
  {% endembed %}

  {% tag 'div' with {
    id: 'image-mode--img',
    class: field.imageMode != 'img' ? 'hidden' : null,
  } %}
    {% embed '_includes/forms/field.twig' with {
        id: 'default-upload-location',
        label: 'Default Upload Location'|t('app'),
        instructions: 'Where images should be stored when they are uploaded directly to the field via drag & drop.'|t('ckeditor'),
    } %}
      {% block input %}
        {% import '_includes/forms.twig' as forms %}
          <div class="flex flex-nowrap">
            <div>
              {{ forms.select({
                id: "default-upload-location-volume",
                name: "defaultUploadLocationVolume",
                options: [
                  {
                    label: 'None'|t('app'),
                    value: '',
                  },
                  ...volumeOptions,
                ],
                value: field.defaultUploadLocationVolume,
                disabled: readOnly,
              }) }}
            </div>
            <div id="default-upload-location-subpath-container" class="flex-grow {{ not field.defaultUploadLocationVolume ? 'hidden' }}">
              {{ forms.text({
                class: 'ltr',
                id: "default-upload-location-subpath",
                name: "defaultUploadLocationSubpath",
                value: field.defaultUploadLocationSubpath,
                placeholder: "path/to/subfolder"|t('app'),
                disabled: readOnly,
              }) }}
            </div>
          </div>
      {% endblock %}
    {% endembed %}

    {{ forms.checkboxSelectField({
      id: 'availableTransforms',
      name: 'availableTransforms',
      label: 'Available Transforms'|t('ckeditor'),
      instructions: 'The transforms that should be available when inserting images.'|t('ckeditor'),
      options: transformOptions,
      values: field.availableTransforms,
      showAllOption: transformOptions|length ? true : false,
      disabled: readOnly,
    }) }}

    {{ forms.selectField({
      id: 'defaultTransform',
      name: 'defaultTransform',
      label: 'Default Transform'|t('ckeditor'),
      instructions: 'The default transform that should be applied when inserting an image.'|t('ckeditor'),
      options: defaultTransformOptions,
      value: field.defaultTransform,
      disabled: readOnly,
    }) }}
  {% endtag %}

  {% tag 'div' with {
    id: 'image-mode--entries',
    class: field.imageMode != 'entries' ? 'hidden' : null,
  } %}
    {% include 'ckeditor/_image-field-select.twig' %}
  {% endtag %}
</div>

<hr>

<a class="fieldtoggle" data-target="advanced">{{ "Advanced"|t('ckeditor') }}</a>
<div id="advanced" class="hidden">
  {% embed '_includes/forms/field.twig' with {
    label: 'Config Options'|t('ckeditor'),
    instructions: 'Define custom [config options]({link}) which should be merged with the default config.'|t('ckeditor', {
      link: 'https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html',
    }),
    id: 'config-options',
    field,
    errors: [
      ...field.getErrors('json'),
      ...field.getErrors('js'),
      ...field.getErrors('jsFile'),
    ],
    data: {'error-key': 'json'},
  } %}
    {% block input %}
      {% import 'codeeditor/codeEditor.twig' as codeEditor %}
      {% import '_includes/forms.twig' as forms %}

      <div id="config-options">
        <section class="btngroup btngroup--exclusive small" aria-label="{{ 'CKEditor config option mode'|t('ckeditor') }}">
          {{ tag('button', {
            type: 'button',
            class: ['btn', 'small', configMode == 'json' ? 'active']|filter,
            aria: {pressed: configMode == 'json' ? 'true'}|filter,
            data: {mode: 'json'},
            text: 'JSON',
          }) }}
          {{ tag('button', {
            type: 'button',
            class: ['btn', 'small', configMode == 'js' ? 'active' : null]|filter,
            aria: {pressed: configMode == 'js' ? 'true'}|filter,
            data: {mode: 'js'},
            text: 'JavaScript',
          }) }}
          {{ tag('button', {
            type: 'button',
            class: ['btn', 'small', configMode == 'file' ? 'active' : null]|filter,
            aria: {pressed: configMode == 'file' ? 'true'}|filter,
            data: {mode: 'file'},
            text: 'File',
          }) }}
        </section>
        <input type="hidden" id="config-options-mode" name="configMode" value="{{ configMode }}">
      </div>

      {% tag 'div' with {
        id: 'config-options-json-container',
        class: {
          'mt-s': true,
          hidden: configMode != 'json',
          disabled: readOnly,
        }|filter|keys,
      } %}
        {% set wrapperClasses = wrapperClasses|push(field.hasErrors('json') ? 'has-errors') %}
        {{ codeEditor.textarea(
          {
            id: 'config-options-json',
            name: 'json',
            value: field.json ?? "{\n  \n}",
          },
          'ckeditor:EditorConfigJson',
          baseMonacoOptions|merge({
            language: 'json',
          }),
          baseCodeEditorOptions|merge({wrapperClass: wrapperClasses|join(' ')}),
        ) }}
      {% endtag %}

      {% tag 'div' with {
        id: 'config-options-js-container',
        class: {
          'mt-s': true,
          hidden: configMode != 'js',
          disabled: readOnly,
        }|filter|keys,
      } %}
        {{ codeEditor.textarea(
          {
            id: 'config-options-js',
            name: 'js',
            value: field.js ?? "return {\n  \n}",
          },
          'ckeditor:EditorConfigJs',
          baseMonacoOptions|merge({
            language: 'javascript',
          }),
          baseCodeEditorOptions|merge({wrapperClass: wrapperClasses|join(' ')}),
        ) }}
      {% endtag %}

      {% tag 'div' with {
        id: 'config-options-file-container',
        class: {
          'mt-s': true,
          hidden: configMode != 'file',
          disabled: readOnly,
        }|filter|keys,
      } %}
        {% if jsFileOptions|length %}
          <p class="light mb-xs">
            {{ 'Choose a JS/JSON file within `config/ckeditor/`.'|t('ckeditor')|md(inlineOnly=true) }}
          </p>
          {{ forms.select({
            id: 'config-options-file',
            name: 'jsFile',
            options: jsFileOptions,
            value: field.jsFile,
          }) }}
        {% else %}
          <p class="warning with-icon">
            {{ 'No JS/JSON files exist within `config/ckeditor/`.'|t('ckeditor')|md(inlineOnly=true) }}
          </p>
        {% endif %}
      {% endtag %}
    {% endblock %}
  {% endembed %}

  {% embed '_includes/forms/field.twig' with {
    label: 'Custom Styles'|t('ckeditor'),
    instructions: 'Define CSS styles that should be registered for editors, such as [style classes]({url}).'|t('ckeditor', {
      url: 'https://ckeditor.com/docs/ckeditor5/latest/features/style.html',
    }),
    id: 'css',
    field,
    errors: [
      ...field.getErrors('css'),
      ...field.getErrors('cssFile'),
    ],
    data: {'error-key': 'css'},
  } %}
    {% block input %}
      {% import 'codeeditor/codeEditor.twig' as codeEditor %}
      {% import '_includes/forms.twig' as forms %}

      <div id="css-options">
        <section class="btngroup btngroup--exclusive small" aria-label="{{ 'CSS mode'|t('ckeditor') }}">
          {{ tag('button', {
            type: 'button',
            class: ['btn', 'small', cssMode == 'css' ? 'active']|filter,
            aria: {pressed: cssMode == 'css' ? 'true'}|filter,
            data: {mode: 'css'},
            text: 'CSS',
          }) }}
          {{ tag('button', {
            type: 'button',
            class: ['btn', 'small', cssMode == 'file' ? 'active' : null]|filter,
            aria: {pressed: cssMode == 'file' ? 'true'}|filter,
            data: {mode: 'file'},
            text: 'File',
          }) }}
        </section>
        <input type="hidden" id="css-options-mode" name="cssMode" value="{{ cssMode }}">
      </div>

      {% tag 'div' with {
        id: 'css-options-css-container',
        class: {
          'mt-s': true,
          hidden: cssMode != 'css',
          disabled: readOnly,
        }|filter|keys,
      } %}
        {% set wrapperClasses = wrapperClasses|push(field.hasErrors('css') ? 'has-errors') %}
        {{ codeEditor.textarea(
          {
            id: 'css-options-css',
            name: 'css',
            value: cssMode == 'css' ? field.css,
          },
          'CodeEditor',
          baseMonacoOptions|merge({
            language: 'css',
          }),
          baseCodeEditorOptions|merge({wrapperClass: wrapperClasses|join(' ')}),
        ) }}
      {% endtag %}

      {% tag 'div' with {
        id: 'css-options-file-container',
        class: {
          'mt-s': true,
          hidden: cssMode != 'file',
          disabled: readOnly,
        }|filter|keys,
      } %}
        {% if cssFileOptions|length %}
          <p class="light mb-xs">
            {{ 'Choose a CSS file within `config/ckeditor/`.'|t('ckeditor')|md(inlineOnly=true) }}
          </p>
          {{ forms.select({
            id: 'css-options-file',
            name: 'cssFile',
            options: cssFileOptions,
            value: field.cssFile,
          }) }}
        {% else %}
          <p class="warning with-icon">
            {{ 'No CSS files exist within `config/ckeditor/`.'|t('ckeditor')|md(inlineOnly=true) }}
          </p>
        {% endif %}
      {% endtag %}
    {% endblock %}
  {% endembed %}

  <script type="module">
    import {ConfigOptions, CssOptions, ToolbarBuilder} from '@craftcms/ckeditor-config';
    {{ importStatements|raw }}

    function initializeConfig() {
      const configOptions = new ConfigOptions(
        {{ configOptionsId|json_encode|raw }},
        {{ jsonSchemaUri|json_encode|raw }},
        {{ configMode|json_encode|raw }},
        {{ (jsFileOptions is not empty)|json_encode|raw }}
      );

      new ToolbarBuilder(
        "{{ toolbarBuilderId }}",
        configOptions,
        [{{ plugins | join(',') }}]
      );

      new CssOptions(
        {{ cssOptionsId|json_encode|raw }},
        {{ cssMode|json_encode|raw }},
        {{ (cssFileOptions is not empty)|json_encode|raw }}
      );

      (() => {
        // Register the config options JSON schema
        const jsonSchemaUri = {{ jsonSchemaUri|json_encode|raw }};
        const schema = JSON.parse(JSON.stringify({{ jsonSchema|json_encode|raw }}));

        monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
          validate: true,
          schemas: [
            {
              uri: jsonSchemaUri,
              fileMatch: [jsonSchemaUri],
              schema: schema,
            },
          ],
        });
      })();
    }

    /**
     * This is not ideal, but when loading the config in a slideout (seemingly only in FireFox)
     * this module code can run `window.monacoEditorInstances` has been created.
     */
    function checkWindow() {
      if (typeof window.monacoEditorInstances === 'undefined') {
        setTimeout(checkWindow, 100);
      } else {
        initializeConfig();
      }
    }

    checkWindow();
  </script>

  {{ forms.lightswitchField({
    label: "Purify HTML"|t('ckeditor'),
    instructions: 'Removes any potentially-malicious code on save, by running the submitted data through {link}.'|t('ckeditor', {
      link: '<a href="http://htmlpurifier.org/" rel="noopener" target="_blank">HTML Purifier</a>',
    }),
    warning: 'Disable this at your own risk!'|t('ckeditor'),
    id: 'purify-html',
    name: 'purifyHtml',
    on: field.purifyHtml,
    toggle: 'purifier-config-container',
    disabled: readOnly,
  }) }}

  <div id="purifier-config-container"{% if not field.purifyHtml %} class="hidden"{% endif %}>
    {{ forms.selectField({
      label: "HTML Purifier Config"|t('ckeditor'),
      instructions: "You can save custom {name} configs as {ext} files in {path}."|t('ckeditor', {
        name: 'HTML Purifier Config',
        ext: '`.json`',
        path: '`config/htmlpurifier/`'
      }) ~
      ' <a href="http://htmlpurifier.org/live/configdoc/plain.html" rel="noopener" target="_blank">'~"View available settings"|t('ckeditor')~'</a>',
      id: 'purifier-config',
      name: 'purifierConfig',
      options: purifierConfigOptions,
      value: field.purifierConfig,
      disabled: readOnly,
    }) }}
  </div>

  {{ forms.lightswitchField({
    id: 'parse-embeds',
    name: 'parseEmbeds',
    label: 'Parse embeds'|t('ckeditor'),
    instructions: 'Whether `<oembed>` tags should be parsed and replaced with the provider’s embed code.'|t('ckeditor'),
    warning: 'This should only be enabled if the field output is cached.'|t('ckeditor'),
    on: field.parseEmbeds,
    disabled: readOnly,
  }) }}

  {% if userGroupOptions|length %}
    <div id="source-editing-groups">
      {{ forms.checkboxSelectField({
        id: 'sourceEditingGroups',
        name: 'sourceEditingGroups',
        label: 'Who should see the “Source” button?'|t('ckeditor'),
        options: userGroupOptions,
        values: field.sourceEditingGroups,
        allLabel: 'All users'|t('app'),
        showAllOption: true,
        disabled: readOnly,
      }) }}
    </div>
  {% endif %}

  {% if craft.app.config.general.enableGql %}
    {{ forms.selectField({
      label: 'GraphQL Mode'|t('app'),
      id: 'graphql-mode',
      name: 'graphqlMode',
      options: [
        {label: 'Full data'|t('app'), value: 'full'},
        {label: 'Raw content only'|t('ckeditor'), value: 'raw'},
      ],
      value: field.fullGraphqlData ? 'full' : 'raw',
      disabled: readOnly,
    }) }}
  {% endif %}
</div>

{% if not readOnly %}
  {% js %}
(() => {
  new Craft.AssetsFieldSettings(
    null,
    {{ 'availableVolumes'|namespaceInputId|json_encode|raw }},
    {{ 'default-upload-location-volume'|namespaceInputId|json_encode|raw }},
    null,
  );

  const $volumeSelect = $("#{{ 'default-upload-location-volume'|namespaceInputId }}");
  const $subpathContainer = $("#{{ 'default-upload-location-subpath-container'|namespaceInputId }}");
  $volumeSelect.on('change', () => {
    $subpathContainer.toggleClass('hidden', !$volumeSelect.val());
  });
})();
  {% endjs %}

  {% script with {type: 'module'} %}
const $select = $("#{{ 'entry-types'|namespaceInputId }}");
// the entry type select gets instantiated asynchronously
const entryTypeSelect = await (() => {
  return new Promise(resolve => {
    const interval = setInterval(() => {
      const entryTypeSelect = $select.data('componentSelect');
      if (entryTypeSelect) {
        clearInterval(interval);
        resolve(entryTypeSelect);
      }
    }, 100);
  });
})();

const updateImageField = () => {
  const entryTypes = Object.values(Garnish.getPostData(entryTypeSelect.$container));
  Craft.sendActionRequest('POST', 'ckeditor/field-settings/render-image-field', {
    data: {
      namespace: {{ view.getNamespace()|json_encode|raw }},
      entryTypes,
      value: $("#{{ 'image-field'|namespaceInputId }}").val(),
    },
  }).then(({data}) => {
    $("#{{ 'image-field-field'|namespaceInputId }}").replaceWith(data.html);
  });
};

entryTypeSelect.on('change', () => updateImageField());
entryTypeSelect.on('applySettings', () => updateImageField());
  {% endscript %}
{% endif %}
