import { Component, Element, Event, h, Host, Listen, Prop, State } from '@stencil/core';
const getTag = (type) => {
  switch (type) {
    case 'h1':
    case 'h2':
    case 'h3':
      return 'dvn-rich-editor-heading-block';
    case 'text':
      return 'dvn-rich-editor-text-block';
    default:
      return 'div';
  }
};
const defaultEditorSettings = {
  enableDrag: true,
  enableBlockCreation: true,
  enableBlockDeletion: true,
  hideDragControls: false,
  blockTypes: ['h1', 'h2', 'h3', 'text'],
  blockSettings: {
    text: {
      textSelectionOptions: ['bold', 'italic', 'strike', 'underline', 'link', 'unlink'],
    }
  },
};
export class DvnRichEditor {
  constructor() {
    this.refs = {};
    this.components = [];
    this.isDragging = false;
    this.value = [
      {
        type: 'text',
        id: generateID(16),
        value: {
          placeholder: 'Start composing your masterpiece.',
          text: '',
        },
      },
    ];
    this.lastUpdatedComponent = undefined;
    this.onDragStart = (component) => {
      this.draggedComponent = component;
      this.isDragging = true;
    };
    this.onDragEnd = () => {
      this.isDragging = false;
    };
    this.onDrop = (toIndex) => {
      this.isDragging = false;
      const { components } = this;
      const fromIndex = components.findIndex(c => c.id === this.draggedComponent.id);
      const newList = [...components];
      newList.splice(fromIndex, 1);
      newList.splice(toIndex, 0, this.draggedComponent);
      this.components = newList;
      this.change.emit(this.components);
    };
    this.onAddNewItemClick = (type, toIndex) => {
      if (!this.getEditorSettings().enableBlockCreation) {
        return;
      }
      const { components } = this;
      let component;
      switch (type) {
        case 'h1':
          component = { type, value: { text: '' } };
          break;
        case 'h2':
          component = { type, value: { text: '' } };
          break;
        case 'h3':
          component = { type, value: { text: '' } };
          break;
        case 'text':
          component = { type, value: { text: '' } };
          break;
      }
      component.id = generateID(16);
      const newList = [...components];
      newList.splice(toIndex, 0, component);
      this.components = newList;
      // focus on the new component
      setTimeout(() => { var _a, _b; return (_b = (_a = this.refs[component.id]).focusOnInput) === null || _b === void 0 ? void 0 : _b.call(_a); }, 300);
    };
    this.onDeleteItemClick = (index) => {
      if (!this.getEditorSettings().enableBlockDeletion) {
        return;
      }
      const { components } = this;
      const newList = [...components];
      newList.splice(index, 1);
      this.components = newList;
      if (index > 0) {
        // focus on the previous component
        const componentToFocus = this.refs[components[index - 1].id];
        setTimeout(() => { var _a; return (_a = componentToFocus.focusOnInput) === null || _a === void 0 ? void 0 : _a.call(componentToFocus); }, 300);
      }
      else if (index + 1 <= components.length) {
        // focus on the next component
        const componentToFocus = this.refs[components[index + 1].id];
        setTimeout(() => { var _a; return (_a = componentToFocus.focusOnInput) === null || _a === void 0 ? void 0 : _a.call(componentToFocus); }, 300);
      }
    };
    this.onConvertItemClick = async (index, type) => {
      var _a, _b;
      const { components } = this;
      const newList = [...components];
      newList[index].type = type;
      // get raw value without html tags in it
      newList[index].value.text = await ((_b = (_a = this.refs[newList[index].id]).getRawValue) === null || _b === void 0 ? void 0 : _b.call(_a)) || newList[index].value.text;
      newList[index].id = generateID(16);
      this.components = newList;
    };
    this.focusOnNextItem = (index) => {
      var _a;
      const { components } = this;
      if (index + 1 < components.length) {
        // focus on the next component
        const componentToFocus = this.refs[components[index + 1].id];
        (_a = componentToFocus.focusOnInput) === null || _a === void 0 ? void 0 : _a.call(componentToFocus);
      }
    };
    this.focusOnPreviousItem = (index) => {
      var _a;
      const { components } = this;
      if (index > 0) {
        // focus on the next component
        const componentToFocus = this.refs[components[index - 1].id];
        (_a = componentToFocus.focusOnInput) === null || _a === void 0 ? void 0 : _a.call(componentToFocus);
      }
    };
    this.getEditorSettings = () => (Object.assign(Object.assign({}, defaultEditorSettings), (this.editorSettings || {})));
  }
  componentWillLoad() {
    this.components = this.value;
  }
  componentDidUpdate() {
    var _a, _b;
    (_b = (_a = this.lastUpdatedComponent) === null || _a === void 0 ? void 0 : _a.focusOnInput) === null || _b === void 0 ? void 0 : _b.call(_a);
  }
  onAction(event) {
    const isItTheEventThisComponentDispatched = event.target['nodeName'].toLowerCase() === 'dvn-rich-editor';
    if (isItTheEventThisComponentDispatched) {
      return;
    }
    this.lastUpdatedComponent = event.target;
    event.stopImmediatePropagation();
    const data = event.detail;
    this.components = this.components.map(component => {
      if (component.id === data.id) {
        return Object.assign(Object.assign({}, component), { value: data.value });
      }
      return component;
    });
    this.change.emit(this.components);
  }
  render() {
    const { isDragging } = this;
    const editorSettings = this.getEditorSettings();
    const blocks = [
      createDropArea(editorSettings, 0, this.onDrop, this.onAddNewItemClick, isDragging)
    ];
    this.components.forEach((component, i) => {
      const componentArea = renderComponentArea(i, component, editorSettings, el => {
        if (el)
          this.refs[component.id] = el;
      }, this.onConvertItemClick, this.onAddNewItemClick, this.onDeleteItemClick, this.focusOnNextItem, this.focusOnPreviousItem);
      if (!editorSettings.enableDrag) {
        blocks.push(componentArea);
      }
      else {
        blocks.push(h("dvn-draggable-box", { key: component.id, onDragEnd: this.onDragEnd, onDragStart: () => this.onDragStart(component) }, componentArea));
      }
      blocks.push(createDropArea(editorSettings, i + 1, this.onDrop, this.onAddNewItemClick, isDragging));
    });
    return (h(Host, null, blocks));
  }
  static get is() { return "dvn-rich-editor"; }
  static get originalStyleUrls() { return {
    "$": ["dvn-rich-editor.scss"]
  }; }
  static get styleUrls() { return {
    "$": ["dvn-rich-editor.css"]
  }; }
  static get properties() { return {
    "value": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "any[]",
        "resolved": "any[]",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "defaultValue": "[\n    {\n      type: 'text',\n      id: generateID(16),\n      value: {\n        placeholder: 'Start composing your masterpiece.',\n        text: '',\n      },\n    },\n  ]"
    },
    "editorSettings": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "EditorSettings",
        "resolved": "{ enableDrag?: boolean; enableBlockCreation?: boolean; enableBlockDeletion?: boolean; hideDragControls?: boolean; blockTypes?: ComponentType[]; blockSettings?: { text?: { textSelectionOptions?: OverlayMenuActionName[]; }; }; }",
        "references": {
          "EditorSettings": {
            "location": "import",
            "path": "@dvn/alaca/components/dvn-rich-editor/constants"
          }
        }
      },
      "required": false,
      "optional": true,
      "docs": {
        "tags": [],
        "text": ""
      }
    }
  }; }
  static get states() { return {
    "components": {},
    "draggedComponent": {},
    "isDragging": {}
  }; }
  static get events() { return [{
      "method": "change",
      "name": "change",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": ""
      },
      "complexType": {
        "original": "any",
        "resolved": "any",
        "references": {}
      }
    }]; }
  static get elementRef() { return "el"; }
  static get listeners() { return [{
      "name": "change",
      "method": "onAction",
      "target": undefined,
      "capture": false,
      "passive": false
    }]; }
}
const renderComponentArea = (index, component, editorSettings, setRef, onConvertItemClick, onAddNewItemClick, onDeleteItemClick, focusOnNextItem, focusOnPreviousItem) => {
  const Tag = getTag(component.type);
  return (h("div", { class: "component-area" },
    h("div", { class: "drag-controls", style: { marginTop: getTopOffset(component.type), width: editorSettings.hideDragControls ? '0' : undefined } },
      editorSettings.enableDrag &&
        h("div", { class: "drag-icon" },
          h("dvn-icon-button", { iconName: "uil-draggabledots", view: "flat", leaned: true, tagMode: true })),
      h("dvn-rich-editor-component-options", { editorSettings: editorSettings, componentType: component.type, onActionClick: (action) => {
          if (action === 'delete') {
            onDeleteItemClick(index);
          }
          else {
            onConvertItemClick(index, action);
          }
        } })),
    h(Tag, { key: component.id, id: component.id, value: component.value, type: component.type, editorSettings: editorSettings, ref: setRef, onEnterKeyDown: () => onAddNewItemClick('text', index + 1), onLastBackspaceKeyDown: () => onDeleteItemClick(index), onArrowDown: () => focusOnNextItem(index), onArrowUp: () => focusOnPreviousItem(index) })));
};
const createDropArea = (editorSettings, index, onDrop, onAddNewItemClick, isDragging) => {
  if (isDragging) {
    return (h("dvn-droppable-box", { key: `drop-area-${index}`, onDrop: () => onDrop(index) },
      h("div", { class: "drop-area" },
        h("div", { class: "drop-area-line" }))));
  }
  if (!editorSettings.enableBlockCreation || editorSettings.blockTypes.length === 0) {
    // just show the area as a placeholder for --content-gap-size
    return h("div", { class: "insert-item-area" });
  }
  return (h("div", { class: "insert-item-area" },
    h("div", { class: "insert-item-area-controls" },
      h("dvn-rich-editor-new-component-options", { index: index, availableBlockTypes: editorSettings.blockTypes, onActionClick: onAddNewItemClick })),
    h("div", { class: "insert-item-area-line" })));
};
function generateID(length) {
  let result = '';
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}
const getTopOffset = (componentType) => {
  switch (componentType) {
    case 'h1':
      return '8px';
    case 'h2':
      return '4px';
    default:
      return undefined;
  }
};
