"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useFloatingPanelState = useFloatingPanelState;
var _react = require("react");
var _hooks = require("../hooks");
var _useFloatingPanelRoot = require("./use-floating-panel-root");
var _types = require("./types");
var _constants = require("./constants");
const step = 80;
const dockAreaWidth = 26;
const delta = {
  [_constants.MOVE_KEYS.UP]: {
    dx: 0,
    dy: -step
  },
  [_constants.MOVE_KEYS.RIGHT]: {
    dx: step,
    dy: 0
  },
  [_constants.MOVE_KEYS.DOWN]: {
    dx: 0,
    dy: step
  },
  [_constants.MOVE_KEYS.LEFT]: {
    dx: -step,
    dy: 0
  }
};
function useFloatingPanelState({
  minWidth,
  minHeight,
  maxWidth,
  maxHeight,
  isDocked: isDockedExternal,
  setIsDocked: setIsDockedExternal,
  isCollapsed: isCollapsedExternal,
  setIsCollapsed: setIsCollapsedExternal,
  initialState,
  containerElement,
  pageContentElement
}) {
  const [state, setState] = (0, _react.useState)(() => {
    if (isDockedExternal) {
      return _types.PANEL_STATE.DOCKED;
    }
    if (isCollapsedExternal) {
      return _types.PANEL_STATE.COLLAPSED;
    }
    return _types.PANEL_STATE.FLOATING;
  });
  const [isMoving, setIsMoving] = (0, _react.useState)(false);
  const isDocked = state === _types.PANEL_STATE.DOCKED;
  const isCollapsed = state === _types.PANEL_STATE.COLLAPSED;
  const [position, setPosition] = (0, _react.useState)(initialState ? {
    top: initialState.top,
    left: initialState.left,
    width: between(initialState.width, minWidth, maxWidth),
    height: between(initialState.height, minHeight, maxHeight)
  } : {
    top: 0,
    left: 0,
    width: minWidth,
    height: minHeight
  });
  const animationRef = (0, _react.useRef)(null);
  const headerRef = (0, _react.useRef)(null);
  const headerHeightRef = (0, _react.useRef)(36);
  const lastActiveElementRef = (0, _react.useRef)();
  const panelRef = (0, _react.useRef)(null);
  const dragStartPositionsRef = (0, _react.useRef)({
    width: minWidth,
    height: minHeight,
    left: 0,
    top: 0,
    mouseX: 0,
    mouseY: 0
  });
  const containerRef = (0, _react.useRef)(containerElement);
  const lastPositionRef = (0, _react.useRef)(position);
  const lastExpandedPositionRef = (0, _react.useRef)(position);
  const stateRef = (0, _react.useRef)(state);
  stateRef.current = state;
  lastPositionRef.current = position;
  containerRef.current = containerElement;
  const style = (0, _react.useMemo)(() => isDocked ? {} : {
    transform: `translate3d(${position.left}px, ${position.top}px, 0px)`,
    width: `${position.width}px`,
    height: `${position.height}px`
  }, [position, isDocked]);
  const runStateTransition = (0, _react.useCallback)(nextState => {
    setState(nextState);
    const getKey = (from, to) => `${from}->${to}`;
    const transitionPairs = {
      [getKey(_types.PANEL_STATE.FLOATING, _types.PANEL_STATE.COLLAPSED)]: () => {
        setIsCollapsedExternal(true);
      },
      [getKey(_types.PANEL_STATE.COLLAPSED, _types.PANEL_STATE.FLOATING)]: () => {
        setIsCollapsedExternal(false);
      },
      [getKey(_types.PANEL_STATE.FLOATING, _types.PANEL_STATE.DOCKED)]: () => {
        setIsDockedExternal(true);
      },
      [getKey(_types.PANEL_STATE.DOCKED, _types.PANEL_STATE.FLOATING)]: () => {
        setIsDockedExternal(false);
        setPosition(state => ({
          ...state,
          top: dragStartPositionsRef.current?.top,
          left: dragStartPositionsRef.current?.left,
          width: lastPositionRef.current.width,
          height: lastPositionRef.current.height
        }));
      },
      [getKey(_types.PANEL_STATE.COLLAPSED, _types.PANEL_STATE.DOCKED)]: () => {
        setIsCollapsedExternal(false);
        setIsDockedExternal(true);
      },
      [getKey(_types.PANEL_STATE.DOCKED, _types.PANEL_STATE.COLLAPSED)]: () => {
        setIsCollapsedExternal(true);
        setIsDockedExternal(false);
      },
      [getKey(_types.PANEL_STATE.FLOATING, _types.PANEL_STATE.TO_BE_DOCKED)]: () => {
        toggleDockHighlight(true, containerRef.current);
      },
      [getKey(_types.PANEL_STATE.TO_BE_DOCKED, _types.PANEL_STATE.FLOATING)]: () => {
        toggleDockHighlight(false, containerRef.current);
        setIsDockedExternal(false);
      },
      [getKey(_types.PANEL_STATE.TO_BE_DOCKED, _types.PANEL_STATE.DOCKED)]: () => {
        setIsDockedExternal(true);
        toggleDockHighlight(false, containerRef.current);
      },
      [getKey(_types.PANEL_STATE.DOCKED, _types.PANEL_STATE.TO_BE_DOCKED)]: () => {
        setIsDockedExternal(false);
        toggleDockHighlight(true, containerRef.current);
      }
    };
    const transition = transitionPairs[getKey(stateRef.current, nextState)];
    if (transition) {
      transition();
    }
  }, [setIsCollapsedExternal, setIsDockedExternal]);
  const onDrag = (0, _react.useCallback)(event => {
    if (animationRef.current) {
      return;
    }
    animationRef.current = requestAnimationFrame(() => {
      const start = dragStartPositionsRef.current;
      const maxLeft = window.innerWidth - start.width * 0.25;
      const maxTop = window.innerHeight - headerHeightRef.current;
      const nextLeft = event.clientX - (start.mouseX - start.left);
      const nextTop = event.clientY - (start.mouseY - start.top);
      setPosition(position => ({
        ...position,
        top: between(nextTop, 0, maxTop),
        left: between(nextLeft, 0, maxLeft)
      }));
      animationRef.current = null;
    });
  }, []);
  const onMovementStart = (0, _react.useCallback)(event => {
    const element = panelRef.current;
    const header = headerRef.current;
    if (!element || !header) {
      return;
    }
    headerHeightRef.current = header.getBoundingClientRect().height;
    const mouseX = event.pageX;
    const mouseY = event.pageY;
    dragStartPositionsRef.current = {
      ...getPanelStateFromElement(element),
      mouseX,
      mouseY
    };
    setIsMoving(true);
    if (!isCollapsed) {
      lastExpandedPositionRef.current = dragStartPositionsRef.current;
    }
  }, [isCollapsed]);
  const onDragEnd = (0, _react.useCallback)(() => {
    if (state === _types.PANEL_STATE.TO_BE_DOCKED) {
      runStateTransition(_types.PANEL_STATE.DOCKED);
    }
    setIsMoving(false);
  }, [state, runStateTransition]);
  const onResize = (0, _react.useCallback)(e => {
    if (animationRef.current) {
      return;
    }
    animationRef.current = requestAnimationFrame(() => {
      const start = dragStartPositionsRef.current;
      const statePatch = {};
      const resize = _constants.resizeHandlesMap.get(e.areaId);
      if (resize?.right) {
        const width = between(start.width + (e.pageX - start.mouseX), minWidth, maxWidth);
        if (width !== start.width) {
          statePatch.width = width;
        }
      }
      if (resize?.bottom) {
        const height = between(start.height + (e.pageY - start.mouseY), minHeight, maxHeight);
        if (height !== start.height) {
          statePatch.height = height;
        }
      }
      if (resize?.top) {
        const height = start.height - (e.pageY - start.mouseY);
        const nextTop = start.top + (e.pageY - start.mouseY);
        const nextTopLimited = between(0, nextTop, window.innerHeight - headerHeightRef.current);
        if (height > minHeight && height <= maxHeight && nextTopLimited > 0) {
          statePatch.height = height;
          statePatch.top = nextTopLimited;
        }
      }
      if (resize?.left) {
        const width = start.width - (e.pageX - start.mouseX);
        if (width > minWidth && width <= maxWidth) {
          statePatch.width = width;
          statePatch.left = start.left + (e.pageX - start.mouseX);
        }
      }
      if (Object.keys(statePatch).length) {
        setPosition(position => ({
          ...position,
          ...statePatch
        }));
      }
      animationRef.current = null;
    });
  }, [minWidth, minHeight, maxWidth, maxHeight]);
  const onResizeEnd = (0, _react.useCallback)(() => {
    setIsMoving(false);
    const nextIsDockHighlighted = isInDockArea({
      ...lastPositionRef.current
    });
    if (nextIsDockHighlighted && state !== _types.PANEL_STATE.DOCKED) {
      runStateTransition(_types.PANEL_STATE.DOCKED);
    }
  }, [state]);
  const setIsDockedHandler = (0, _react.useCallback)(docked => {
    if (docked) {
      const element = panelRef.current;
      dragStartPositionsRef.current = {
        ...getPanelStateFromElement(element),
        mouseX: 0,
        mouseY: 0
      };
      runStateTransition(_types.PANEL_STATE.DOCKED);
    } else {
      runStateTransition(_types.PANEL_STATE.FLOATING);
    }
  }, [runStateTransition]);
  const setIsCollapsedHandler = (0, _react.useCallback)(collapsed => {
    if (collapsed === isCollapsed) {
      return;
    }
    if (collapsed && !isDocked) {
      lastExpandedPositionRef.current = getPanelStateFromElement(panelRef.current);
    }
    if (collapsed) {
      setPosition(position => ({
        ...position,
        top: window.innerHeight - headerHeightRef.current
      }));
    } else {
      setPosition(position => ({
        ...position,
        ...lastExpandedPositionRef.current
      }));
    }
  }, [isCollapsed, isDocked]);
  const onMoveWithArrows = event => {
    if (!_constants.MOVE_KEYS_VALUES.includes(event.key)) {
      return;
    }
    const element = panelRef.current;
    if (!element) {
      return;
    }
    const dx = delta[event.key].dx;
    const dy = delta[event.key].dy;
    const currentState = getPanelStateFromElement(element);
    const maxLeft = window.innerWidth - currentState.width * 0.25;
    const maxTop = window.innerHeight - headerHeightRef.current;
    const nextLeft = currentState.left + dx;
    const nextTop = currentState.top + dy;
    const nextState = {
      ...position,
      top: between(nextTop, 0, maxTop),
      left: between(nextLeft, 0, maxLeft)
    };
    if (nextState.top === lastPositionRef.current.top && nextState.left === lastPositionRef.current.left) {
      return;
    }
    const nextDocked = isInDockArea(nextState);
    if (isDocked && nextDocked === isDocked) {
      return;
    }
    if (nextDocked && !isDocked) {
      dragStartPositionsRef.current = {
        ...currentState,
        mouseX: 0,
        mouseY: 0
      };
      lastExpandedPositionRef.current = dragStartPositionsRef.current;
      runStateTransition(_types.PANEL_STATE.DOCKED);
    } else {
      setPosition(nextState);
    }
  };
  const getPosition = (0, _react.useCallback)(() => {
    return lastPositionRef.current;
  }, []);

  // state transition based on position
  (0, _react.useEffect)(() => {
    const nextCollapsed = isCollapsedPosition(position, headerHeightRef.current);
    const nextIsDockHighlighted = isInDockArea(position);
    if (nextCollapsed && state !== _types.PANEL_STATE.COLLAPSED) {
      runStateTransition(_types.PANEL_STATE.COLLAPSED);
    } else if (nextIsDockHighlighted && ![_types.PANEL_STATE.TO_BE_DOCKED, _types.PANEL_STATE.DOCKED].includes(state)) {
      runStateTransition(_types.PANEL_STATE.TO_BE_DOCKED);
    } else if (!nextCollapsed && !nextIsDockHighlighted && state !== _types.PANEL_STATE.FLOATING) {
      runStateTransition(_types.PANEL_STATE.FLOATING);
    }
  }, [position]);

  // sync up with external state changes
  (0, _react.useEffect)(() => {
    if (isDockedExternal !== isDocked) {
      setIsDockedHandler(isDockedExternal);
    }
  }, [isDockedExternal]);
  (0, _react.useEffect)(() => {
    if (isCollapsedExternal !== isCollapsed) {
      setIsCollapsedHandler(isCollapsedExternal);
    }
  }, [isCollapsedExternal]);

  // update position on window resize
  // initialise position and sizes
  (0, _react.useEffect)(() => {
    const header = headerRef.current;
    headerHeightRef.current = header.getBoundingClientRect().height;
    dragStartPositionsRef.current = {
      ...getPanelStateFromElement(panelRef.current),
      mouseX: 0,
      mouseY: 0
    };
    const onResizeHandler = () => {
      const start = lastPositionRef.current;
      const maxLeft = window.innerWidth - start.width;
      const maxTop = window.innerHeight - headerHeightRef.current;
      if (start.left > maxLeft || start.top > maxTop) {
        setPosition(state => ({
          ...state,
          top: between(start.top, 0, maxTop),
          left: between(start.left, 0, maxLeft)
        }));
      }
    };
    window.addEventListener('resize', onResizeHandler);
    return () => {
      window.removeEventListener('resize', onResizeHandler);
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
        animationRef.current = null;
      }
    };
  }, []);

  // this allows to return focus to the last active element
  (0, _react.useLayoutEffect)(() => {
    lastActiveElementRef.current = document.activeElement;
    const onEscapeHandler = e => {
      if (e.key === 'Escape') {
        (lastActiveElementRef.current || document.body).focus();
      }
    };
    window.addEventListener('keydown', onEscapeHandler);
    return () => {
      window.addEventListener('keydown', onEscapeHandler);
    };
  }, []);
  (0, _hooks.useFocusTrap)(panelRef);
  // update style according to content and header height
  (0, _useFloatingPanelRoot.useFloatingPanelRoot)({
    containerElement,
    pageContentElement
  });
  const context = {
    onDrag,
    onDragStart: onMovementStart,
    onDragEnd,
    onMoveWithArrows,
    isDocked,
    setIsDocked: setIsDockedHandler,
    isCollapsed,
    setIsCollapsed: setIsCollapsedHandler,
    getPosition,
    isMoving
  };
  return {
    style,
    panelRef,
    headerRef,
    onResize,
    onMovementStart,
    onResizeEnd,
    setPosition,
    context: (0, _react.useMemo)(() => context, Object.values(context)),
    isDocked,
    setIsDocked: setIsDockedHandler,
    isCollapsed,
    setIsCollapsed: setIsCollapsedHandler,
    getPosition
  };
}
function getPanelStateFromElement(element) {
  if (!element) {
    return {
      width: 0,
      height: 0,
      left: 0,
      top: 0
    };
  }
  const bbox = element.getBoundingClientRect();
  return {
    width: bbox.width,
    height: bbox.height,
    left: bbox.x,
    top: bbox.y
  };
}
function isInDockArea(panelPosition) {
  return panelPosition.left + panelPosition.width > window.innerWidth - dockAreaWidth;
}
function isCollapsedPosition(panelPosition, headerHeight) {
  return window.innerHeight - panelPosition.top <= headerHeight;
}
function toggleDockHighlight(isDockHighlighted, element) {
  if (!element) {
    return;
  }
  if (isDockHighlighted) {
    element.classList.add(_constants.HIGHLIGHTED_CLASSNAME);
  } else {
    element.classList.remove(_constants.HIGHLIGHTED_CLASSNAME);
  }
}
function between(value, min, max) {
  return Math.min(Math.max(value, min), max);
}