import { invariant } from "../../invariant";
import getDraggablesInsideDroppable from "../get-draggables-inside-droppable";
import { add, patch } from "../position";
import getSubject from "./util/get-subject";
import isHomeOf from "./is-home-of";
import getDisplacedBy from "../get-displaced-by";

const getRequiredGrowthForPlaceholder = (droppable, placeholderSize, draggables) => {
  const axis = droppable.axis;

  // A virtual list will most likely not contain all of the Draggables
  // so counting them does not help.
  if (droppable.descriptor.mode === "virtual") {
    return patch(axis.line, placeholderSize[axis.line]);
  }

  // TODO: consider margin collapsing?
  // Using contentBox as that is where the Draggables will sit
  const availableSpace = droppable.subject.page.contentBox[axis.size];
  const insideDroppable = getDraggablesInsideDroppable(droppable.descriptor.id, draggables);
  const spaceUsed = insideDroppable.reduce((sum, dimension) => sum + dimension.client.marginBox[axis.size], 0);
  const requiredSpace = spaceUsed + placeholderSize[axis.line];
  const needsToGrowBy = requiredSpace - availableSpace;

  // nothing to do here
  if (needsToGrowBy <= 0) {
    return null;
  }
  return patch(axis.line, needsToGrowBy);
};
const withMaxScroll = (frame, max) => ({
  ...frame,
  scroll: {
    ...frame.scroll,
    max
  }
});
export const addPlaceholder = (droppable, draggable, draggables) => {
  const frame = droppable.frame;
  invariant(!isHomeOf(draggable, droppable), "Should not add placeholder space to home list");
  invariant(!droppable.subject.withPlaceholder, "Cannot add placeholder size to a subject when it already has one");
  const placeholderSize = getDisplacedBy(droppable.axis, draggable.displaceBy).point;
  const requiredGrowth = getRequiredGrowthForPlaceholder(droppable, placeholderSize, draggables);
  const added = {
    placeholderSize,
    increasedBy: requiredGrowth,
    oldFrameMaxScroll: droppable.frame ? droppable.frame.scroll.max : null
  };
  if (!frame) {
    const subject = getSubject({
      page: droppable.subject.page,
      withPlaceholder: added,
      axis: droppable.axis,
      frame: droppable.frame
    });
    return {
      ...droppable,
      subject
    };
  }
  const maxScroll = requiredGrowth ? add(frame.scroll.max, requiredGrowth) : frame.scroll.max;
  const newFrame = withMaxScroll(frame, maxScroll);
  const subject = getSubject({
    page: droppable.subject.page,
    withPlaceholder: added,
    axis: droppable.axis,
    frame: newFrame
  });
  return {
    ...droppable,
    subject,
    frame: newFrame
  };
};
export const removePlaceholder = (droppable) => {
  const added = droppable.subject.withPlaceholder;
  invariant(added, "Cannot remove placeholder form subject when there was none");
  const frame = droppable.frame;
  if (!frame) {
    const subject = getSubject({
      page: droppable.subject.page,
      axis: droppable.axis,
      frame: null,
      // cleared
      withPlaceholder: null
    });
    return {
      ...droppable,
      subject
    };
  }
  const oldMaxScroll = added.oldFrameMaxScroll;
  invariant(oldMaxScroll, "Expected droppable with frame to have old max frame scroll when removing placeholder");
  const newFrame = withMaxScroll(frame, oldMaxScroll);
  const subject = getSubject({
    page: droppable.subject.page,
    axis: droppable.axis,
    frame: newFrame,
    // cleared
    withPlaceholder: null
  });
  return {
    ...droppable,
    subject,
    frame: newFrame
  };
};
