/*
 * https://github.com/morethanwords/tweb
 * Copyright (C) 2019-2021 Eduard Kuzmenko
 * https://github.com/morethanwords/tweb/blob/master/LICENSE
 */

import contextMenuController from '../helpers/contextMenuController';
import cancelEvent from '../helpers/dom/cancelEvent';
import {AttachClickOptions, CLICK_EVENT_NAME, hasMouseMovedSinceDown} from '../helpers/dom/clickEvent';
import ListenerSetter from '../helpers/listenerSetter';
import ButtonIcon from './buttonIcon';
import ButtonMenu, {ButtonMenuItemOptionsVerifiable} from './buttonMenu';
import filterAsync from '../helpers/array/filterAsync';
import {doubleRaf} from '../helpers/schedulers';

// TODO: refactor for attachClickEvent, because if move finger after touchstart, it will start anyway
export function ButtonMenuToggleHandler({
  el,
  onOpen,
  options,
  onClose
}: {
  el: HTMLElement,
  onOpen?: (e: Event) => any,
  options?: AttachClickOptions,
  onClose?: () => void
}) {
  const add = options?.listenerSetter ? options.listenerSetter.add(el) : el.addEventListener.bind(el);

  add(CLICK_EVENT_NAME, (e: Event) => {
    if(!el.classList.contains('btn-menu-toggle') || hasMouseMovedSinceDown(e)) return false;

    cancelEvent(e);

    if(el.classList.contains('menu-open')) {
      contextMenuController.close();
    } else {
      const result = onOpen?.(e);
      const open = () => {
        const openedMenu = el.querySelector('.btn-menu') as HTMLDivElement;
        contextMenuController.openBtnMenu(openedMenu, onClose);
      };

      if(result instanceof Promise) {
        result.then(open);
      } else {
        open();
      }
    }
  });
}

export default function ButtonMenuToggle({
  buttonOptions,
  listenerSetter: attachListenerSetter,
  container,
  direction,
  buttons,
  onOpenBefore,
  onOpen,
  onClose,
  onCloseAfter
}: {
  buttonOptions?: Parameters<typeof ButtonIcon>[1],
  listenerSetter?: ListenerSetter,
  container?: HTMLElement
  direction: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right',
  buttons: ButtonMenuItemOptionsVerifiable[],
  onOpenBefore?: (e: Event) => any,
  onOpen?: (e: Event, element: HTMLElement) => any,
  onClose?: () => void,
  onCloseAfter?: () => void
}) {
  if(buttonOptions) {
    buttonOptions.asDiv = true;
  }

  const button = container ?? ButtonIcon('more', buttonOptions);
  button.classList.add('btn-menu-toggle');

  const listenerSetter = new ListenerSetter();

  let element: HTMLElement, closeTimeout: number;
  ButtonMenuToggleHandler({
    el: button,
    onOpen: async(e) => {
      await onOpenBefore?.(e);
      if(closeTimeout) {
        clearTimeout(closeTimeout);
        closeTimeout = undefined;
        return;
      }

      const f = (b: (typeof buttons[0])[]) => filterAsync(b, (button) => button?.verify?.() ?? true);

      const filteredButtons = await f(buttons);
      if(!filteredButtons.length) {
        return;
      }

      const _element = element = await ButtonMenu({
        buttons: filteredButtons,
        listenerSetter
      });
      _element.classList.add(direction);

      await onOpen?.(e, _element);

      button.append(_element);
      await doubleRaf();
    },
    options: {
      listenerSetter: attachListenerSetter
    },
    onClose: () => {
      onClose?.();

      closeTimeout = window.setTimeout(() => {
        onCloseAfter?.();
        closeTimeout = undefined;
        listenerSetter.removeAll();
        buttons.forEach((button) => button.element = undefined);
        element.remove();
      }, 300);
    }
  });

  return button;
}
