import { dropAnimationFinished } from "../../action-creators";
import bindEvents from "../../../view/event-bindings/bind-events";

const dropAnimationFlushOnScrollMiddleware = (store) => {
  let unbind = null;
  let frameId = null;

  function clear() {
    if (frameId) {
      cancelAnimationFrame(frameId);
      frameId = null;
    }
    if (unbind) {
      unbind();
      unbind = null;
    }
  }

  return (next) => (action) => {
    if (action.type === "FLUSH" || action.type === "DROP_COMPLETE" || action.type === "DROP_ANIMATION_FINISHED") {
      clear();
    }
    next(action);
    if (action.type !== "DROP_ANIMATE") {
      return;
    }
    const binding = {
      eventName: "scroll",
      // capture: true will catch all scroll events, event from scroll containers
      // once: just in case, we only want to ever fire one
      options: {
        capture: true,
        passive: false,
        once: true
      },
      fn: function flushDropAnimation() {
        const state = store.getState();
        if (state.phase === "DROP_ANIMATING") {
          store.dispatch(dropAnimationFinished());
        }
      }
    };

    // The browser can batch a few scroll events in a single frame
    // including the one that ended the drag.
    // Binding after a requestAnimationFrame ensures that any scrolls caused
    // by the auto scroller are finished
    // TODO: why is a second window scroll being fired?
    // It leads to funny drop positions :(
    frameId = requestAnimationFrame(() => {
      frameId = null;
      unbind = bindEvents(window, [binding]);
    });
  };
};

export default dropAnimationFlushOnScrollMiddleware;
