/**
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 */

import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
import Factory from 'tinymce/core/api/ui/Factory';
import Tools from 'tinymce/core/api/util/Tools';
import UiContainer from '../alien/UiContainer';
import Events from '../api/Events';
import Settings from '../api/Settings';
import Layout from '../core/Layout';
import Measure from '../core/Measure';
import Forms from './Forms';
import Toolbar from './Toolbar';
import { Editor } from 'tinymce/core/api/Editor';
import { ContextToolbar } from 'tinymce/themes/inlite/core/Render';

export interface InlitePanel {
  show: (editor: Editor, id: string, targetRect, toolbars: ContextToolbar[]) => void;
  showForm: (editor: Editor, id: string) => void;
  reposition: (editor: Editor, id: string, targetRect) => void;
  inForm: () => boolean;
  hide: () => void;
  focus: () => void;
  remove: () => void;
}

export const create = (): InlitePanel => {
  let panel, currentRect;

  const createToolbars = function (editor: Editor, toolbars: ContextToolbar[]) {
    return Tools.map(toolbars, function (toolbar: ContextToolbar) {
      return Toolbar.create(editor, toolbar.id, toolbar.items);
    });
  };

  const hasToolbarItems = function (toolbar) {
    return toolbar.items().length > 0;
  };

  const create = function (editor: Editor, toolbars: ContextToolbar[]) {
    const items = createToolbars(editor, toolbars).concat([
      Toolbar.create(editor, 'text', Settings.getTextSelectionToolbarItems(editor)),
      Toolbar.create(editor, 'insert', Settings.getInsertToolbarItems(editor)),
      Forms.createQuickLinkForm(editor, hide)
    ]);

    return Factory.create({
      type: 'floatpanel',
      role: 'dialog',
      classes: 'tinymce tinymce-inline arrow',
      ariaLabel: 'Inline toolbar',
      layout: 'flex',
      direction: 'column',
      align: 'stretch',
      autohide: false,
      autofix: true,
      fixed: true,
      border: 1,
      items: Tools.grep(items, hasToolbarItems),
      oncancel () {
        editor.focus();
      }
    });
  };

  const showPanel = function (panel) {
    if (panel) {
      panel.show();
    }
  };

  const movePanelTo = function (panel, pos) {
    panel.moveTo(pos.x, pos.y);
  };

  const togglePositionClass = function (panel, relPos) {
    relPos = relPos ? relPos.substr(0, 2) : '';

    Tools.each({
      t: 'down',
      b: 'up',
      c: 'center'
    }, function (cls, pos) {
      panel.classes.toggle('arrow-' + cls, pos === relPos.substr(0, 1));
    });

    if (relPos === 'cr') {
      panel.classes.toggle('arrow-left', true);
      panel.classes.toggle('arrow-right', false);
    } else if (relPos === 'cl') {
      panel.classes.toggle('arrow-left', false);
      panel.classes.toggle('arrow-right', true);
    } else {
      Tools.each({
        l: 'left',
        r: 'right'
      }, function (cls, pos) {
        panel.classes.toggle('arrow-' + cls, pos === relPos.substr(1, 1));
      });
    }
  };

  const showToolbar = function (panel, id) {
    const toolbars = panel.items().filter('#' + id);

    if (toolbars.length > 0) {
      toolbars[0].show();
      panel.reflow();
      return true;
    }

    return false;
  };

  const repositionPanelAt = function (panel, id, editor, targetRect) {
    let contentAreaRect, panelRect, result, userConstainHandler;

    userConstainHandler = Settings.getPositionHandler(editor);
    contentAreaRect = Measure.getContentAreaRect(editor);
    panelRect = DOMUtils.DOM.getRect(panel.getEl());

    if (id === 'insert') {
      result = Layout.calcInsert(targetRect, contentAreaRect, panelRect);
    } else {
      result = Layout.calc(targetRect, contentAreaRect, panelRect);
    }

    if (result) {
      const delta = UiContainer.getUiContainerDelta().getOr({ x: 0, y: 0 });
      const transposedPanelRect = { x: result.rect.x - delta.x, y: result.rect.y - delta.y, w: result.rect.w, h: result.rect.h };
      currentRect = targetRect;
      movePanelTo(panel, Layout.userConstrain(userConstainHandler, targetRect, contentAreaRect, transposedPanelRect));
      togglePositionClass(panel, result.position);
      return true;
    } else {
      return false;
    }
  };

  const showPanelAt = function (panel, id, editor, targetRect) {
    showPanel(panel);
    panel.items().hide();

    if (!showToolbar(panel, id)) {
      hide();
      return;
    }

    if (repositionPanelAt(panel, id, editor, targetRect) === false) {
      hide();
    }
  };

  const hasFormVisible = function () {
    return panel.items().filter('form:visible').length > 0;
  };

  const showForm = function (editor: Editor, id) {
    if (panel) {
      panel.items().hide();

      if (!showToolbar(panel, id)) {
        hide();
        return;
      }

      let contentAreaRect, panelRect, result, userConstainHandler;

      showPanel(panel);
      panel.items().hide();
      showToolbar(panel, id);

      userConstainHandler = Settings.getPositionHandler(editor);
      contentAreaRect = Measure.getContentAreaRect(editor);
      panelRect = DOMUtils.DOM.getRect(panel.getEl());

      result = Layout.calc(currentRect, contentAreaRect, panelRect);

      if (result) {
        panelRect = result.rect;
        movePanelTo(panel, Layout.userConstrain(userConstainHandler, currentRect, contentAreaRect, panelRect));
        togglePositionClass(panel, result.position);
      }
    }
  };

  const show = function (editor: Editor, id: string, targetRect, toolbars) {
    if (!panel) {
      Events.fireBeforeRenderUI(editor);
      panel = create(editor, toolbars);
      panel.renderTo().reflow().moveTo(targetRect.x, targetRect.y);
      editor.nodeChanged();
    }

    showPanelAt(panel, id, editor, targetRect);
  };

  const reposition = function (editor: Editor, id: string, targetRect) {
    if (panel) {
      repositionPanelAt(panel, id, editor, targetRect);
    }
  };

  const hide = function () {
    if (panel) {
      panel.hide();
    }
  };

  const focus = function () {
    if (panel) {
      panel.find('toolbar:visible').eq(0).each(function (item) {
        item.focus(true);
      });
    }
  };

  const remove = function () {
    if (panel) {
      panel.remove();
      panel = null;
    }
  };

  const inForm = function () {
    return panel && panel.visible() && hasFormVisible();
  };

  return {
    show,
    showForm,
    reposition,
    inForm,
    hide,
    focus,
    remove
  };
};