import { createEditor, Element, Editor, Range, Transforms, Text, Node, Path } from 'slate';

export const withUnit = (editor) => {
  const {
    onChange,
    onKeydown,
    onError,
    onClick,
    insertTextData,
    insertText,
    insertSoftBreak,
    insertNode,
    insertFragmentData,
    insertFragment,
    deleteBackward,
    deleteCutData,
    deleteForward,
    deleteFragment,
    insertBreak,
    insertData,
    isInline,
    isVoid,
  } = editor;

  editor.isInline = (element) => {
    return element && element.unit === true;
  };

  editor.onChange = (d) => {
    // repair the selection, do not select half of the emoji, only the whole block or nothing
    if (editor.selection) {
      let repairNodeSelectionStart;
      let repairNodeSelectionEnd;
      if (isUnit(editor.selection.anchor.path)) {
        repairNodeSelectionStart = getPositionOutFromUnit(editor.selection.anchor);
      }
      if (isUnit(editor.selection.focus.path)) {
        repairNodeSelectionEnd = getPositionOutFromUnit(editor.selection.focus);
      }

      if (repairNodeSelectionStart || repairNodeSelectionEnd) {
        Transforms.select(editor, {
          anchor: repairNodeSelectionStart || editor.selection.anchor,
          focus: repairNodeSelectionEnd || editor.selection.focus,
        });
      }
    }

    onChange(d);
  };

  /**
   * If the cursor at the end of the emoji, go to the next, else go to the prev element
   * check always the isEmoji first
   * This function only works if the selection in an emoji
   */
  let getPositionOutFromUnit = (unitLocation: { path; offset }) => {
    let resultPath;
    let resultOffset;

    let unitNodePath;
    let parentPath = Path.parent(unitLocation.path);
    if (Node.get(editor, parentPath)['unit']) {
      unitNodePath = parentPath;
    } else {
      unitNodePath = unitLocation.path;
    }

    if (unitLocation.offset == 0) {
      resultPath = Path.previous(unitNodePath);
      resultOffset = Node.get(editor, resultPath)['text']['length'];
    } else {
      resultPath = Path.next(unitNodePath);
      resultOffset = 0;
    }
    return {
      path: resultPath,
      offset: resultOffset,
    };
  };

  let isUnit = (path: Path) => {
    return Node.get(editor, path)['unit'] || Node.get(editor, Path.parent(path))['unit'];
  };

  editor.deleteBackward = (unit) => {
    //console.log('delete bw', unit);
    /**
     * Check if we have to delete an emoji
     */
    if (isUnit(editor.selection.anchor.path)) {
      let path = getPositionOutFromUnit(editor.selection.anchor);
      Transforms.select(editor, {
        anchor: path,
        focus: path,
      });
    }

    if (editor.selection.anchor.offset == 0) {
      let prevPath;
      try {
        // throw error when selection negative position
        prevPath = Path.previous(editor.selection.anchor.path);
      } catch (e) {
        deleteBackward(unit);
        return;
      }
      if (Node.has(editor, prevPath) && isUnit(prevPath)) {
        Transforms.delete(editor, { at: prevPath });
        return;
      }
    }

    deleteBackward(unit);
    return;
  };

  editor.deleteForward = (unit) => {
    //console.log('delete fw');

    if (isUnit(editor.selection.anchor.path)) {
      let path = getPositionOutFromUnit(editor.selection.anchor);
      Transforms.select(editor, {
        anchor: path,
        focus: path,
      });
    }

    if (
      Node.get(editor, editor.selection.anchor.path)['text']['length'] ==
      editor.selection.anchor.offset
    ) {
      let nextPath = Path.next(editor.selection.anchor.path);
      if (Node.has(editor, nextPath) && isUnit(nextPath)) {
        Transforms.delete(editor, { at: nextPath });
        return;
      }
    }

    deleteForward(unit);
  };

  // called when deleting a range
  editor.deleteFragment = (direction) => {
    let startLocation;
    let endLocation;

    if (Path.compare(editor.selection.anchor.path, editor.selection.focus.path) == -1) {
      startLocation = editor.selection.anchor;
      endLocation = editor.selection.focus;
    } else {
      startLocation = editor.selection.focus;
      endLocation = editor.selection.anchor;
    }

    if (isUnit(startLocation.path)) {
      startLocation = getPositionOutFromUnit(startLocation);
    }
    if (isUnit(endLocation.path)) {
      endLocation = getPositionOutFromUnit(endLocation);
    }

    //console.log('from start', startLocation.path, endLocation.path);
    Transforms.select(editor, {
      anchor: startLocation,
      focus: endLocation,
    });
    deleteFragment(direction);
  };

  /*editor.insertText = (text) => {
    let { selection } = editor;

    if (
      selection &&
      editor.children[selection.anchor.path[0]] &&
      editor.children[selection.anchor.path[0]].type == 'paragraph'
    ) {
      if (!Range.isCollapsed(selection)) {
        Transforms.delete(editor);
        Transforms.collapse(editor);
      }

      let tokensFromSelection = Node.get(editor, editor.selection.anchor.path)['token'];

      const tokens = tokenizeMarkdownLines(text);

      if (isEmoji(editor.selection.anchor.path)) {
        let path = getPositionOutFromEmoji(editor.selection.anchor);
        Transforms.select(editor, {
          anchor: path,
          focus: path,
        });
      }

      let nodes = [];
      tokens.forEach((t) => {
        let isEmoji = t.tokens.includes(TOKEN_TYPE.EMOJI);
        let isEmojiShortcode = t.tokens.includes(TOKEN_TYPE.EMOJI_SHORTCODE);
        if (isEmoji) {
          nodes.push({
            type: 'emoji',
            alt: t.text,
            src: twemoji.convert.toCodePoint(t.text),
            children: [{ text: t.text }],
            token: [TOKEN_TYPE.EMOJI], // for the markdown tokenizer
          });
        } else if (
          isEmojiShortcode &&
          emojiShortcodeToUnicode[t.text.slice(1, t.text.length - 1)]
        ) {
          nodes.push({
            type: 'emoji',
            alt: t.text,
            src: twemoji.convert.toCodePoint(
              emojiShortcodeToUnicode[t.text.slice(1, t.text.length - 1)]
            ),
            children: [{ text: t.text }],
            token: [TOKEN_TYPE.EMOJI_SHORTCODE], // for the markdown tokenizer
          });
        } else {
          let textNode = { text: t.text };
          if (tokensFromSelection || t.tokens.length > 0) {
            textNode['token'] = t.tokens.concat(tokensFromSelection || []);
          }
          nodes.push(textNode);
        }
      });

      Transforms.insertNodes(editor, nodes);
      insertText('');
    } else {
      insertText(text);
    }
  };*/

  editor.insertText = (text) => {
    let { selection } = editor;

    // prevent to grab+move content inside the emoji
    if (
      selection &&
      editor.children[selection.anchor.path[0]] &&
      editor.children[selection.anchor.path[0]].type == 'paragraph'
    ) {
      if (!Range.isCollapsed(selection)) {
        Transforms.delete(editor);
        Transforms.collapse(editor);
      }

      if (isUnit(editor.selection.anchor.path)) {
        let path = getPositionOutFromUnit(editor.selection.anchor);
        Transforms.select(editor, {
          anchor: path,
          focus: path,
        });
      }
    }

    insertText(text);
  };

  editor.insertFragmentData = (data) => {
    //console.log('insertFragmentData', data);
    //insertFragmentData(data); // prevent
  };

  editor.insertData = (data) => {
    //console.log('insertData', data);

    let { selection } = editor;

    // prevent to grab+move content inside the emoji
    if (
      selection &&
      editor.children[selection.anchor.path[0]] &&
      editor.children[selection.anchor.path[0]].type == 'paragraph'
    ) {
      if (!Range.isCollapsed(selection)) {
        Transforms.delete(editor);
        Transforms.collapse(editor);
      }

      if (isUnit(editor.selection.anchor.path)) {
        let path = getPositionOutFromUnit(editor.selection.anchor);
        Transforms.select(editor, {
          anchor: path,
          focus: path,
        });
      }
    }
    //editor.insertText(data.getData('text/plain'));
    insertData(data); // if not called, the other paster data cb-s wont get called
  };
  /*
  // not recommended by api
  editor.deleteCutData = (d) => {
    console.log('deleteCurData', d);
    deleteCutData(d);
  };
  editor.insertData = (data) => {
    console.log('insertData', data);
    //editor.insertText(data.getData('text/plain'));
    insertData(data); // if not called, the other paster data cb-s wont get called
  }; 

  editor.insertSoftBreak = (d) => {
    console.log('insertSoftBreak', d);
    insertSoftBreak(d);
  };

  editor.insertTextData = (data) => {
    console.log('insertTextData', data);
    insertTextData(data); // insert fragment already handle it
  };

  editor.onClick = (d) => {
    console.log('onClick', d);
    onClick(d);
  };
  editor.onError = (errorData) => {
    console.log('onError', errorData);
    onError(errorData);
  };
  
  editor.insertBreak = (d) => {
    console.log('insertBreak', d);
    insertBreak(d);
  };
  editor.insertFragment = (fragment) => {
    console.log('insertFragment', fragment);
    insertFragment(fragment);
  };
  editor.insertNode = (node) => {
    console.log('insertNode', node);
    insertNode(node);
  };*/
  /*editor.onKeydown = (d) => {
        console.log('onKeydown', d);
        onKeydown(d);
    };
    */

  return editor;
};
