import {
  ref,
  Ref,
  onMounted,
  onBeforeUnmount,
  defineComponent,
  computed,
} from 'vue';
import debounce from 'lodash/debounce';
import { useRootElement } from '../use-root-element';
import { useFormattingToolbar } from './use-formatting-toolbar';
import {
  getSelection,
  getAnchorElementForSelection,
  getRangeFromSelection,
  getRect,
} from './selection';

import './FormattingToolbar.scss';

export const SbFormattingToolbar = defineComponent({
  name: 'sb-inlinetoolbar',

  setup() {
    const { rootElement } = useRootElement();
    const {
      activeClient,
      setActiveClient,
      clients,
      getAllTools,
    } = useFormattingToolbar();

    const allTools = computed(() => getAllTools());

    const selection: Ref<Selection|null>= ref(null);
    const selectionRect: Ref<DOMRect|null>= ref(null);
    const updateSelectionRect = () => {
      selectionRect.value = getRect(selection.value!);
    };

    const showing: Ref<boolean> = ref(false);

    const show = () => {
      showing.value = true;
      updateSelectionRect();
    };
    const hide = () => {
      showing.value = false;
      setActiveClient(null);
    };

    const style = computed(() => {
      const rootRect = rootElement.value?.getBoundingClientRect();
      const x = (selectionRect.value?.x || 0)
              - (rootRect?.left || 0);
      const y = (selectionRect.value?.y || 0)
              + (selectionRect.value?.height || 0)
              - (rootRect?.top || 0);
      return {
        left: Math.floor(x) + 'px',
        top: Math.floor(y) + 'px',
      };
    });

    const classes = computed(() => ({
      'sb-inline-toolbar': true,
    }));

    const onSelectionChanged = debounce(() => {
      selection.value = getSelection();
      if (!selection.value) {
        console.warn('Could not get selection');
        return
      }

      const range = getRangeFromSelection(selection.value);
      // If we're not selecting anything, bail
      if (!range || range.endOffset === range.startOffset) {
        hide();
        return;
      }

      const focusedElement = document.activeElement;
      if (!focusedElement) {
        hide();
        return;
      }

      // If new selection is not in registered clients, close it
      if (!clients.value.find(client => client === focusedElement)) {
        hide();
        return;
      }

      setActiveClient(document.activeElement as HTMLElement|null);

      show();
    }, 50);

    onMounted(() => {
      rootElement.value?.addEventListener('click', close);
      document.addEventListener('selectionchange', onSelectionChanged, true);
    });

    onBeforeUnmount(() => {
      rootElement.value?.removeEventListener('click', close);
      document.removeEventListener('selectionchange', onSelectionChanged);
    });

    return () => (allTools.value.length && showing.value)
      ? <div
        style={style.value}
        class={classes.value}
        onClick={(event:MouseEvent) => { event.stopPropagation(); }}
    >{allTools.value.map((tool) => {
      const ToolUI = tool.ui as any;
      return <ToolUI
        activeClient={activeClient}
        selection={selection.value}
      ></ToolUI>;
    })}</div>
      : null;
  },
});
