import { getWindow, getDocument } from 'ssr-window';
import $ from '../../shared/dom7.js';
import { extend, deleteProps } from '../../shared/utils.js';
import Framework7Class from '../../shared/class.js';
import { getDevice } from '../../shared/get-device.js';
const textEditorButtonsMap = {
  // f7-icon, material-icon, command
  bold: ['bold', 'format_bold', 'bold'],
  italic: ['italic', 'format_italic', 'italic'],
  underline: ['underline', 'format_underlined', 'underline'],
  strikeThrough: ['strikethrough', 'strikethrough_s', 'strikeThrough'],
  orderedList: ['list_number', 'format_list_numbered', 'insertOrderedList'],
  unorderedList: ['list_bullet', 'format_list_bulleted', 'insertUnorderedList'],
  link: ['link', 'link', 'createLink'],
  image: ['photo', 'image', 'insertImage'],
  paragraph: ['paragraph', '<i class="icon">¶</i>', 'formatBlock.P'],
  h1: ['<i class="icon">H<sub>1</sub></i>', '<i class="icon">H<sub>1</sub></i>', 'formatBlock.H1'],
  h2: ['<i class="icon">H<sub>2</sub></i>', '<i class="icon">H<sub>2</sub></i>', 'formatBlock.H2'],
  h3: ['<i class="icon">H<sub>3</sub></i>', '<i class="icon">H<sub>3</sub></i>', 'formatBlock.H3'],
  alignLeft: ['text_alignleft', 'format_align_left', 'justifyLeft'],
  alignCenter: ['text_aligncenter', 'format_align_center', 'justifyCenter'],
  alignRight: ['text_alignright', 'format_align_right', 'justifyRight'],
  alignJustify: ['text_justify', 'format_align_justify', 'justifyFull'],
  subscript: ['textformat_subscript', '<i class="icon">A<sub>1</sub></i>', 'subscript'],
  superscript: ['textformat_superscript', '<i class="icon">A<sup>1</sup></i>', 'superscript'],
  indent: ['increase_indent', 'format_indent_increase', 'indent'],
  outdent: ['decrease_indent', 'format_indent_decrease', 'outdent']
};
class TextEditor extends Framework7Class {
  constructor(app, params) {
    super(params, [app]);
    const self = this;
    const document = getDocument();
    const device = getDevice();
    const defaults = extend({}, app.params.textEditor);

    // Extend defaults with modules params
    self.useModulesParams(defaults);
    self.params = extend(defaults, params);
    const el = self.params.el;
    if (!el) return self;
    const $el = $(el);
    if ($el.length === 0) return self;
    if ($el[0].f7TextEditor) return $el[0].f7TextEditor;
    let $contentEl = $el.children('.text-editor-content');
    if (!$contentEl.length) {
      $el.append('<div class="text-editor-content" contenteditable></div>');
      $contentEl = $el.children('.text-editor-content');
    }
    extend(self, {
      app,
      $el,
      el: $el[0],
      $contentEl,
      contentEl: $contentEl[0]
    });
    if ('value' in params) {
      self.value = self.params.value;
    }
    if (self.params.mode === 'keyboard-toolbar') {
      if (!(device.cordova || device.capacitor) && !device.android) {
        self.params.mode = 'popover';
      }
    }
    if (typeof self.params.buttons === 'string') {
      try {
        self.params.buttons = JSON.parse(self.params.buttons);
      } catch (err) {
        throw new Error('Framework7: TextEditor: wrong "buttons" parameter format');
      }
    }
    $el[0].f7TextEditor = self;

    // Bind
    self.onButtonClick = self.onButtonClick.bind(self);
    self.onFocus = self.onFocus.bind(self);
    self.onBlur = self.onBlur.bind(self);
    self.onInput = self.onInput.bind(self);
    self.onPaste = self.onPaste.bind(self);
    self.onSelectionChange = self.onSelectionChange.bind(self);
    self.closeKeyboardToolbar = self.closeKeyboardToolbar.bind(self);

    // Handle Events
    self.attachEvents = function attachEvents() {
      if (self.params.mode === 'toolbar') {
        self.$el.find('.text-editor-toolbar').on('click', 'button', self.onButtonClick);
      }
      if (self.params.mode === 'keyboard-toolbar') {
        self.$keyboardToolbarEl.on('click', 'button', self.onButtonClick);
        self.$el.parents('.page').on('page:beforeout', self.closeKeyboardToolbar);
      }
      if (self.params.mode === 'popover' && self.popover) {
        self.popover.$el.on('click', 'button', self.onButtonClick);
      }
      self.$contentEl.on('paste', self.onPaste);
      self.$contentEl.on('focus', self.onFocus);
      self.$contentEl.on('blur', self.onBlur);
      self.$contentEl.on('input', self.onInput, true);
      $(document).on('selectionchange', self.onSelectionChange);
    };
    self.detachEvents = function detachEvents() {
      if (self.params.mode === 'toolbar') {
        self.$el.find('.text-editor-toolbar').off('click', 'button', self.onButtonClick);
      }
      if (self.params.mode === 'keyboard-toolbar') {
        self.$keyboardToolbarEl.off('click', 'button', self.onButtonClick);
        self.$el.parents('.page').off('page:beforeout', self.closeKeyboardToolbar);
      }
      if (self.params.mode === 'popover' && self.popover) {
        self.popover.$el.off('click', 'button', self.onButtonClick);
      }
      self.$contentEl.off('paste', self.onPaste);
      self.$contentEl.off('focus', self.onFocus);
      self.$contentEl.off('blur', self.onBlur);
      self.$contentEl.off('input', self.onInput, true);
      $(document).off('selectionchange', self.onSelectionChange);
    };

    // Install Modules
    self.useModules();

    // Init
    self.init();
    return self;
  }
  setValue(newValue) {
    const self = this;
    const currentValue = self.value;
    if (currentValue === newValue) return self;
    self.value = newValue;
    self.$contentEl.html(newValue);
    self.$el.trigger('texteditor:change', self.value);
    self.emit('local::change textEditorChange', self, self.value);
    return self;
  }
  getValue() {
    const self = this;
    return self.value;
  }
  clearValue() {
    const self = this;
    self.setValue('');
    if (self.params.placeholder && !self.$contentEl.html()) {
      self.insertPlaceholder();
    }
    return self;
  }
  createLink() {
    const self = this;
    const window = getWindow();
    const document = getDocument();
    const currentSelection = window.getSelection();
    const selectedNodes = [];
    let $selectedLinks;
    if (currentSelection && currentSelection.anchorNode && $(currentSelection.anchorNode).parents(self.$el).length) {
      let anchorNode = currentSelection.anchorNode;
      while (anchorNode) {
        selectedNodes.push(anchorNode);
        if (!anchorNode.nextSibling || anchorNode === currentSelection.focusNode) {
          anchorNode = null;
        }
        if (anchorNode) {
          anchorNode = anchorNode.nextSibling;
        }
      }
      const selectedNodesLinks = [];
      const $selectedNodes = $(selectedNodes);
      for (let i = 0; i < $selectedNodes.length; i += 1) {
        const childNodes = $selectedNodes[i].children;
        if (childNodes) {
          for (let j = 0; j < childNodes.length; j += 1) {
            if ($(childNodes[j]).is('a')) {
              selectedNodesLinks.push(childNodes[j]);
            }
          }
        }
      }
      $selectedLinks = $selectedNodes.closest('a').add($(selectedNodesLinks));
    }
    if ($selectedLinks && $selectedLinks.length) {
      $selectedLinks.each(linkNode => {
        const selection = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(linkNode);
        selection.removeAllRanges();
        selection.addRange(range);
        document.execCommand('unlink', false);
        selection.removeAllRanges();
      });
      return self;
    }
    const currentRange = self.getSelectionRange();
    if (!currentRange) return self;
    const dialog = self.app.dialog.prompt('', self.params.linkUrlText, link => {
      if (link && link.trim().length) {
        self.setSelectionRange(currentRange);
        document.execCommand('createLink', false, link.trim());
        self.$el.trigger('texteditor:insertlink', {
          url: link.trim()
        });
        self.emit('local:insertLink textEditorInsertLink', self, link.trim());
      }
    });
    dialog.$el.find('input').focus();
    return self;
  }
  insertImage() {
    const self = this;
    const document = getDocument();
    const currentRange = self.getSelectionRange();
    if (!currentRange) return self;
    const dialog = self.app.dialog.prompt('', self.params.imageUrlText, imageUrl => {
      if (imageUrl && imageUrl.trim().length) {
        self.setSelectionRange(currentRange);
        document.execCommand('insertImage', false, imageUrl.trim());
        self.$el.trigger('texteditor:insertimage', {
          url: imageUrl.trim()
        });
        self.emit('local:insertImage textEditorInsertImage', self, imageUrl.trim());
      }
    });
    dialog.$el.find('input').focus();
    return self;
  }
  removePlaceholder() {
    const self = this;
    self.$contentEl.find('.text-editor-placeholder').remove();
  }
  insertPlaceholder() {
    const self = this;
    self.$contentEl.append(`<div class="text-editor-placeholder">${self.params.placeholder}</div>`);
  }
  onSelectionChange() {
    const self = this;
    const window = getWindow();
    const document = getDocument();
    if (self.params.mode === 'toolbar') return;
    const selection = window.getSelection();
    const selectionIsInContent = $(selection.anchorNode).parents(self.contentEl).length || selection.anchorNode === self.contentEl;
    if (self.params.mode === 'keyboard-toolbar') {
      if (!selectionIsInContent) {
        self.closeKeyboardToolbar();
      } else {
        self.openKeyboardToolbar();
      }
      return;
    }
    if (self.params.mode === 'popover') {
      const selectionIsInPopover = $(selection.anchorNode).parents(self.popover.el).length || selection.anchorNode === self.popover.el;
      if (!selectionIsInContent && !selectionIsInPopover) {
        self.closePopover();
        return;
      }
      if (!selection.isCollapsed && selection.rangeCount) {
        const range = selection.getRangeAt(0);
        const rect = range.getBoundingClientRect();
        const rootEl = self.app.$el[0] || document.body;
        self.openPopover(rect.x + (window.scrollX || 0) - rootEl.offsetLeft, rect.y + (window.scrollY || 0) - rootEl.offsetTop, rect.width, rect.height);
      } else if (selection.isCollapsed) {
        self.closePopover();
      }
    }
  }
  onPaste(e) {
    const self = this;
    const document = getDocument();
    if (self.params.clearFormattingOnPaste && e.clipboardData && e.clipboardData.getData) {
      const text = e.clipboardData.getData('text/plain');
      e.preventDefault();
      document.execCommand('insertText', false, text);
    }
  }
  onInput() {
    const self = this;
    const value = self.$contentEl.html();
    self.value = value;
    self.$el.trigger('texteditor:input');
    self.emit('local:input textEditorInput', self, self.value);
    self.$el.trigger('texteditor:change', self.value);
    self.emit('local::change textEditorChange', self, self.value);
  }
  onFocus() {
    const self = this;
    self.removePlaceholder();
    self.$contentEl.focus();
    self.$el.trigger('texteditor:focus');
    self.emit('local::focus textEditorFocus', self);
  }
  onBlur() {
    const self = this;
    const window = getWindow();
    const document = getDocument();
    if (self.params.placeholder && self.$contentEl.html() === '') {
      self.insertPlaceholder();
    }
    if (self.params.mode === 'popover') {
      const selection = window.getSelection();
      const selectionIsInContent = $(selection.anchorNode).parents(self.contentEl).length || selection.anchorNode === self.contentEl;
      const inPopover = document.activeElement && self.popover && $(document.activeElement).closest(self.popover.$el).length;
      if (!inPopover && !selectionIsInContent) {
        self.closePopover();
      }
    }
    if (self.params.mode === 'keyboard-toolbar') {
      const selection = window.getSelection();
      const selectionIsInContent = $(selection.anchorNode).parents(self.contentEl).length || selection.anchorNode === self.contentEl;
      if (!selectionIsInContent) {
        self.closeKeyboardToolbar();
      }
    }
    self.$el.trigger('texteditor:blur');
    self.emit('local::blur textEditorBlur', self);
  }
  onButtonClick(e) {
    const self = this;
    const window = getWindow();
    const document = getDocument();
    const selection = window.getSelection();
    const selectionIsInContent = $(selection.anchorNode).parents(self.contentEl).length || selection.anchorNode === self.contentEl;
    if (!selectionIsInContent) return;
    const $buttonEl = $(e.target).closest('button');
    if ($buttonEl.parents('form').length) {
      e.preventDefault();
    }
    const button = $buttonEl.attr('data-button');
    const buttonData = self.params.customButtons && self.params.customButtons[button];
    if (!button || !(textEditorButtonsMap[button] || buttonData)) return;
    $buttonEl.trigger('texteditor:buttonclick', button);
    self.emit('local::buttonClick textEditorButtonClick', self, button);
    if (buttonData) {
      if (buttonData.onClick) buttonData.onClick(self, $buttonEl[0]);
      return;
    }
    const command = textEditorButtonsMap[button][2];
    if (command === 'createLink') {
      self.createLink();
      return;
    }
    if (command === 'insertImage') {
      self.insertImage();
      return;
    }
    if (command.indexOf('formatBlock') === 0) {
      const tagName = command.split('.')[1];
      const $anchorNode = $(selection.anchorNode);
      if ($anchorNode.parents(tagName.toLowerCase()).length || $anchorNode.is(tagName)) {
        document.execCommand('formatBlock', false, 'div');
      } else {
        document.execCommand('formatBlock', false, tagName);
      }
      return;
    }
    document.execCommand(command, false);
  }

  // eslint-disable-next-line
  getSelectionRange() {
    const window = getWindow();
    const document = getDocument();
    if (window.getSelection) {
      const sel = window.getSelection();
      if (sel.getRangeAt && sel.rangeCount) {
        return sel.getRangeAt(0);
      }
    } else if (document.selection && document.selection.createRange) {
      return document.selection.createRange();
    }
    return null;
  }

  // eslint-disable-next-line
  setSelectionRange(range) {
    const window = getWindow();
    const document = getDocument();
    if (range) {
      if (window.getSelection) {
        const sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
      } else if (document.selection && range.select) {
        range.select();
      }
    }
  }
  renderButtons() {
    const self = this;
    let html = '';
    function renderButton(button) {
      const iconClass = self.app.theme === 'md' ? 'material-icons' : 'f7-icons';
      if (self.params.customButtons && self.params.customButtons[button]) {
        const buttonData = self.params.customButtons[button];
        return `<button type="button" class="text-editor-button" data-button="${button}">${buttonData.content || ''}</button>`;
      }
      if (!textEditorButtonsMap[button]) return '';
      const iconContent = textEditorButtonsMap[button][self.app.theme === 'md' ? 1 : 0];
      return `<button type="button" class="text-editor-button" data-button="${button}">${iconContent.indexOf('<') >= 0 ? iconContent : `<i class="${iconClass}">${iconContent}</i>`}</button>`.trim();
    }
    self.params.buttons.forEach((button, buttonIndex) => {
      if (Array.isArray(button)) {
        button.forEach(b => {
          html += renderButton(b);
        });
        if (buttonIndex < self.params.buttons.length - 1 && self.params.dividers) {
          html += '<div class="text-editor-button-divider"></div>';
        }
      } else {
        html += renderButton(button);
      }
    });
    return html;
  }
  createToolbar() {
    const self = this;
    self.$el.prepend(`<div class="text-editor-toolbar">${self.renderButtons()}</div>`);
  }
  createKeyboardToolbar() {
    const self = this;
    self.$keyboardToolbarEl = $(`<div class="toolbar toolbar-bottom text-editor-keyboard-toolbar"><div class="toolbar-inner">${self.renderButtons()}</div></div>`);
  }
  createPopover() {
    const self = this;
    self.popover = self.app.popover.create({
      content: `
        <div class="popover dark text-editor-popover">
          <div class="popover-inner">${self.renderButtons()}</div>
        </div>
      `,
      closeByOutsideClick: false,
      backdrop: false
    });
  }
  openKeyboardToolbar() {
    const self = this;
    if (self.$keyboardToolbarEl.parent(self.app.$el).length) return;
    self.$el.trigger('texteditor:keyboardopen');
    self.emit('local::keyboardOpen textEditorKeyboardOpen', self);
    self.app.$el.append(self.$keyboardToolbarEl);
  }
  closeKeyboardToolbar() {
    const self = this;
    self.$keyboardToolbarEl.remove();
    self.$el.trigger('texteditor:keyboardclose');
    self.emit('local::keyboardClose textEditorKeyboardClose', self);
  }
  openPopover(targetX, targetY, targetWidth, targetHeight) {
    const self = this;
    if (!self.popover) return;
    Object.assign(self.popover.params, {
      targetX,
      targetY,
      targetWidth,
      targetHeight
    });
    clearTimeout(self.popoverTimeout);
    self.popoverTimeout = setTimeout(() => {
      if (!self.popover) return;
      if (self.popover.opened) {
        self.popover.resize();
      } else {
        self.$el.trigger('texteditor:popoveropen');
        self.emit('local::popoverOpen textEditorPopoverOpen', self);
        self.popover.open();
      }
    }, 400);
  }
  closePopover() {
    const self = this;
    clearTimeout(self.popoverTimeout);
    if (!self.popover || !self.popover.opened) return;
    self.popoverTimeout = setTimeout(() => {
      if (!self.popover) return;
      self.$el.trigger('texteditor:popoverclose');
      self.emit('local::popoverClose textEditorPopoverClose', self);
      self.popover.close();
    }, 400);
  }
  init() {
    const self = this;
    if (self.value) {
      self.$contentEl.html(self.value);
    } else {
      self.value = self.$contentEl.html();
    }
    if (self.params.placeholder && self.value === '') {
      self.insertPlaceholder();
    }
    if (self.params.mode === 'toolbar') {
      self.createToolbar();
    } else if (self.params.mode === 'popover') {
      self.createPopover();
    } else if (self.params.mode === 'keyboard-toolbar') {
      self.createKeyboardToolbar();
    }
    self.attachEvents();
    self.$el.trigger('texteditor:init');
    self.emit('local::init textEditorInit', self);
    return self;
  }
  destroy() {
    let self = this;
    self.$el.trigger('texteditor:beforedestroy');
    self.emit('local::beforeDestroy textEditorBeforeDestroy', self);
    self.detachEvents();
    if (self.params.mode === 'keyboard-toolbar' && self.$keyboardToolbarEl) {
      self.$keyboardToolbarEl.remove();
    }
    if (self.popover) {
      self.popover.close(false);
      self.popover.destroy();
    }
    delete self.$el[0].f7TextEditor;
    deleteProps(self);
    self = null;
  }
}
export default TextEditor;