import React, { useEffect, useRef, useState } from "react";
import { useCallback } from "use-memo-one";
import { transitions } from "../../animation";
import { noSpacing } from "../../state/spacing";

function noop() {
  // NO OP
}

const empty = {
  width: 0,
  height: 0,
  margin: noSpacing
};
const getSize = ({ isAnimatingOpenOnMount, placeholder, animate }) => {
  if (isAnimatingOpenOnMount) {
    return empty;
  }
  if (animate === "close") {
    return empty;
  }
  return {
    height: placeholder.client.borderBox.height,
    width: placeholder.client.borderBox.width,
    margin: placeholder.client.margin
  };
};
const getStyle = ({ isAnimatingOpenOnMount, placeholder, animate }) => {
  const size = getSize({
    isAnimatingOpenOnMount,
    placeholder,
    animate
  });
  return {
    display: placeholder.display,
    // ## Recreating the box model
    // We created the borderBox and then apply the margins directly
    // this is to maintain any margin collapsing behaviour

    // creating borderBox
    // background: 'green',
    boxSizing: "border-box",
    width: size.width,
    height: size.height,
    // creating marginBox
    marginTop: size.margin.top,
    marginRight: size.margin.right,
    marginBottom: size.margin.bottom,
    marginLeft: size.margin.left,
    // ## Avoiding collapsing
    // Avoiding the collapsing or growing of this element when pushed by flex child siblings.
    // We have already taken a snapshot the current dimensions we do not want this element
    // to recalculate its dimensions
    // It is okay for these properties to be applied on elements that are not flex children
    flexShrink: "0",
    flexGrow: "0",
    // Just a little performance optimisation: avoiding the browser needing
    // to worry about pointer events for this element
    pointerEvents: "none",
    // Animate the placeholder size and margin
    transition: animate !== "none" ? transitions.placeholder : null
  };
};

function Placeholder(props) {
  const animateOpenTimerRef = useRef(null);
  const tryClearAnimateOpenTimer = useCallback(() => {
    if (!animateOpenTimerRef.current) {
      return;
    }
    clearTimeout(animateOpenTimerRef.current);
    animateOpenTimerRef.current = null;
  }, []);
  const { animate, onTransitionEnd, onClose, contextId } = props;
  const [isAnimatingOpenOnMount, setIsAnimatingOpenOnMount] = useState(props.animate === "open");

  // Will run after a render is flushed
  // Still need to wait a timeout to ensure that the
  // update is completely applied to the DOM
  useEffect(() => {
    // No need to do anything
    if (!isAnimatingOpenOnMount) {
      return noop;
    }

    // might need to clear the timer
    if (animate !== "open") {
      tryClearAnimateOpenTimer();
      setIsAnimatingOpenOnMount(false);
      return noop;
    }

    // timer already pending
    if (animateOpenTimerRef.current) {
      return noop;
    }
    animateOpenTimerRef.current = setTimeout(() => {
      animateOpenTimerRef.current = null;
      setIsAnimatingOpenOnMount(false);
    });

    // clear the timer if needed
    return tryClearAnimateOpenTimer;
  }, [animate, isAnimatingOpenOnMount, tryClearAnimateOpenTimer]);
  const onSizeChangeEnd = useCallback(
    (event) => {
      // We transition height, width and margin
      // each of those transitions will independently call this callback
      // Because they all have the same duration we can just respond to one of them
      // 'height' was chosen for no particular reason :D
      if (event.propertyName !== "height") {
        return;
      }
      onTransitionEnd();
      if (animate === "close") {
        onClose();
      }
    },
    [animate, onClose, onTransitionEnd]
  );
  const style = getStyle({
    isAnimatingOpenOnMount,
    animate: props.animate,
    placeholder: props.placeholder
  });
  return /*#__PURE__*/ React.createElement(props.placeholder.tagName, {
    style,
    "data-rbd-placeholder-context-id": contextId,
    onTransitionEnd: onSizeChangeEnd,
    ref: props.innerRef
  });
}

export default /*#__PURE__*/ React.memo(Placeholder);
// enzyme does not work well with memo, so exporting the non-memo version
export const WithoutMemo = Placeholder;
