import { toDroppableList } from "./dimension-structures";
import isPositionInFrame from "./visibility/is-position-in-frame";
import { distance, patch } from "./position";
import isWithin from "./is-within";

// https://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other
// https://silentmatt.com/rectangle-intersection/
function getHasOverlap(first, second) {
  return (
    first.left < second.right && first.right > second.left && first.top < second.bottom && first.bottom > second.top
  );
}

function getFurthestAway({ draggable, candidates }) {
  // We are not comparing the center of the home list with the target list as it would
  // give preference to giant lists

  // We are measuring the distance from where the draggable started
  // to where it is *hitting* the candidate
  // Note: The hit point might technically not be in the bounds of the candidate

  const startCenter = draggable.page.borderBox.center;
  const sorted = candidates
    .map((candidate) => {
      const axis = candidate.axis;
      const target = patch(
        candidate.axis.line,
        // use the center of the list on the main axis
        candidate.page.borderBox.center[axis.line],
        // use the center of the list on the cross axis
        candidate.page.borderBox.center[axis.crossAxisLine]
      );
      return {
        id: candidate.descriptor.id,
        distance: distance(startCenter, target)
      };
    })
    // largest value will be first
    .sort((a, b) => b.distance - a.distance);

  // just being safe
  return sorted[0] ? sorted[0].id : null;
}

export default function getDroppableOver({ pageBorderBox, draggable, droppables }) {
  // We know at this point that some overlap has to exist
  const candidates = toDroppableList(droppables).filter((item) => {
    // Cannot be a candidate when disabled
    if (!item.isEnabled) {
      return false;
    }

    // Cannot be a candidate when there is no visible area
    const active = item.subject.active;
    if (!active) {
      return false;
    }

    // Cannot be a candidate when dragging item is not over the droppable at all
    if (!getHasOverlap(pageBorderBox, active)) {
      return false;
    }

    // 1. Candidate if the center position is over a droppable
    if (isPositionInFrame(active)(pageBorderBox.center)) {
      return true;
    }

    // 2. Candidate if an edge is over the cross axis half way point
    // 3. Candidate if dragging item is totally over droppable on cross axis

    const axis = item.axis;
    const childCenter = active.center[axis.crossAxisLine];
    const crossAxisStart = pageBorderBox[axis.crossAxisStart];
    const crossAxisEnd = pageBorderBox[axis.crossAxisEnd];
    const isContained = isWithin(active[axis.crossAxisStart], active[axis.crossAxisEnd]);
    const isStartContained = isContained(crossAxisStart);
    const isEndContained = isContained(crossAxisEnd);

    // Dragging item is totally covering the active area
    if (!isStartContained && !isEndContained) {
      return true;
    }

    /**
     * edges must go beyond the center line in order to avoid
     * cases were both conditions are satisfied.
     */
    if (isStartContained) {
      return crossAxisStart < childCenter;
    }
    return crossAxisEnd > childCenter;
  });
  if (!candidates.length) {
    return null;
  }

  // Only one candidate - use that!
  if (candidates.length === 1) {
    return candidates[0].descriptor.id;
  }

  // Multiple options returned
  // Should only occur with really large items
  // Going to use fallback: distance from home
  return getFurthestAway({
    pageBorderBox,
    draggable,
    candidates
  });
}
