import getDragImpact from "../../get-drag-impact";
import { add, subtract } from "../../position";
import recomputePlaceholders from "../../recompute-placeholders";

const update = ({
  state,
  clientSelection: forcedClientSelection,
  dimensions: forcedDimensions,
  viewport: forcedViewport,
  impact: forcedImpact,
  scrollJumpRequest
}) => {
  // DRAGGING: can update position and impact
  // COLLECTING: can update position but cannot update impact

  const viewport = forcedViewport || state.viewport;
  const dimensions = forcedDimensions || state.dimensions;
  const clientSelection = forcedClientSelection || state.current.client.selection;
  const offset = subtract(clientSelection, state.initial.client.selection);
  const client = {
    offset,
    selection: clientSelection,
    borderBoxCenter: add(state.initial.client.borderBoxCenter, offset)
  };
  const page = {
    selection: add(client.selection, viewport.scroll.current),
    borderBoxCenter: add(client.borderBoxCenter, viewport.scroll.current),
    offset: add(client.offset, viewport.scroll.diff.value)
  };
  const current = {
    client,
    page
  };

  // Not updating impact while bulk collecting
  if (state.phase === "COLLECTING") {
    return {
      // adding phase to appease flow (even though it will be overwritten by spread)
      phase: "COLLECTING",
      ...state,
      dimensions,
      viewport,
      current
    };
  }
  const draggable = dimensions.draggables[state.critical.draggable.id];
  const newImpact =
    forcedImpact ||
    getDragImpact({
      pageOffset: page.offset,
      draggable,
      draggables: dimensions.draggables,
      droppables: dimensions.droppables,
      previousImpact: state.impact,
      viewport,
      afterCritical: state.afterCritical
    });
  const withUpdatedPlaceholders = recomputePlaceholders({
    draggable,
    impact: newImpact,
    previousImpact: state.impact,
    draggables: dimensions.draggables,
    droppables: dimensions.droppables
  });
  // dragging!
  const result = {
    ...state,
    current,
    dimensions: {
      draggables: dimensions.draggables,
      droppables: withUpdatedPlaceholders
    },
    impact: newImpact,
    viewport,
    scrollJumpRequest: scrollJumpRequest || null,
    // client updates can be applied as a part of a jump scroll
    // this can be to immediately reverse movement to allow for a nice animation
    // into the final position
    forceShouldAnimate: scrollJumpRequest ? false : null
  };
  return result;
};

export default update;
