import React from "react";
import {
  moveOneLeft,
  moveOneRight,
  moveAllLeft,
  moveAllRight,
} from "../zindex";
import { KEYS, isDarwin } from "../keys";
import { t } from "../i18n";
import { getShortcutKey } from "../utils";
import { register } from "./register";
import {
  SendBackwardIcon,
  BringToFrontIcon,
  SendToBackIcon,
  BringForwardIcon,
} from "../components/icons";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";

const getElementIndices = (
  direction: "left" | "right",
  elements: readonly ExcalidrawElement[],
  appState: AppState,
) => {
  const selectedIndices: number[] = [];
  let deletedIndicesCache: number[] = [];

  const cb = (element: ExcalidrawElement, index: number) => {
    if (element.isDeleted) {
      // we want to build an array of deleted elements that are preceeding
      //  a selected element so that we move them together
      deletedIndicesCache.push(index);
    } else {
      if (appState.selectedElementIds[element.id]) {
        selectedIndices.push(...deletedIndicesCache, index);
      }
      // always empty cache of deleted elements after either pushing a group
      //  of selected/deleted elements, of after encountering non-deleted elem
      deletedIndicesCache = [];
    }
  };

  // sending back → select contiguous deleted elements that are to the left of
  //  selected element(s)
  if (direction === "left") {
    let i = -1;
    const len = elements.length;
    while (++i < len) {
      cb(elements[i], i);
    }
    // moving to front → loop from right to left so that we don't need to
    //  backtrack when gathering deleted elements
  } else {
    let i = elements.length;
    while (--i > -1) {
      cb(elements[i], i);
    }
  }
  // sort in case we were gathering indexes from right to left
  return selectedIndices.sort();
};

const moveElements = (
  func: typeof moveOneLeft,
  elements: readonly ExcalidrawElement[],
  appState: AppState,
) => {
  const _elements = elements.slice();
  const direction =
    func === moveOneLeft || func === moveAllLeft ? "left" : "right";
  const indices = getElementIndices(direction, _elements, appState);
  return func(_elements, indices);
};

export const actionSendBackward = register({
  name: "sendBackward",
  perform: (elements, appState) => {
    return {
      elements: moveElements(moveOneLeft, elements, appState),
      appState,
      commitToHistory: true,
    };
  },
  contextItemLabel: "labels.sendBackward",
  keyPriority: 40,
  keyTest: (event) =>
    event[KEYS.CTRL_OR_CMD] && !event.shiftKey && event.code === "BracketLeft",
  PanelComponent: ({ updateData, appState }) => (
    <button
      type="button"
      className="zIndexButton"
      onClick={() => updateData(null)}
      title={`${t("labels.sendBackward")} — ${getShortcutKey("CtrlOrCmd+[")}`}
    >
      <SendBackwardIcon appearance={appState.appearance} />
    </button>
  ),
});

export const actionBringForward = register({
  name: "bringForward",
  perform: (elements, appState) => {
    return {
      elements: moveElements(moveOneRight, elements, appState),
      appState,
      commitToHistory: true,
    };
  },
  contextItemLabel: "labels.bringForward",
  keyPriority: 40,
  keyTest: (event) =>
    event[KEYS.CTRL_OR_CMD] && !event.shiftKey && event.code === "BracketRight",
  PanelComponent: ({ updateData, appState }) => (
    <button
      type="button"
      className="zIndexButton"
      onClick={() => updateData(null)}
      title={`${t("labels.bringForward")} — ${getShortcutKey("CtrlOrCmd+]")}`}
    >
      <BringForwardIcon appearance={appState.appearance} />
    </button>
  ),
});

export const actionSendToBack = register({
  name: "sendToBack",
  perform: (elements, appState) => {
    return {
      elements: moveElements(moveAllLeft, elements, appState),
      appState,
      commitToHistory: true,
    };
  },
  contextItemLabel: "labels.sendToBack",
  keyTest: (event) => {
    return isDarwin
      ? event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === "BracketLeft"
      : event[KEYS.CTRL_OR_CMD] &&
          event.shiftKey &&
          event.code === "BracketLeft";
  },
  PanelComponent: ({ updateData, appState }) => (
    <button
      type="button"
      className="zIndexButton"
      onClick={() => updateData(null)}
      title={`${t("labels.sendToBack")} — ${
        isDarwin
          ? getShortcutKey("CtrlOrCmd+Alt+[")
          : getShortcutKey("CtrlOrCmd+Shift+[")
      }`}
    >
      <SendToBackIcon appearance={appState.appearance} />
    </button>
  ),
});

export const actionBringToFront = register({
  name: "bringToFront",
  perform: (elements, appState) => {
    return {
      elements: moveElements(moveAllRight, elements, appState),
      appState,
      commitToHistory: true,
    };
  },
  contextItemLabel: "labels.bringToFront",
  keyTest: (event) => {
    return isDarwin
      ? event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === "BracketRight"
      : event[KEYS.CTRL_OR_CMD] &&
          event.shiftKey &&
          event.code === "BracketRight";
  },
  PanelComponent: ({ updateData, appState }) => (
    <button
      type="button"
      className="zIndexButton"
      onClick={(event) => updateData(null)}
      title={`${t("labels.bringToFront")} — ${
        isDarwin
          ? getShortcutKey("CtrlOrCmd+Alt+]")
          : getShortcutKey("CtrlOrCmd+Shift+]")
      }`}
    >
      <BringToFrontIcon appearance={appState.appearance} />
    </button>
  ),
});
