import getTrackIdsBetween, { countTracksBetween } from '../getTrackIdsBetween';
import { getSelectableTrackIds } from '../selectors';
import { AFTER_LAST_TRACK, StudioProjectState } from '../types';
import { combineActions } from './combineActions';

export const assignAnchorAndFocus =
  (mouseDownBeats: number) => (state: StudioProjectState) => {
    const furthestEdge =
      Math.abs(mouseDownBeats - state.selection.focusBeats) >
      Math.abs(mouseDownBeats - state.selection.anchorBeats)
        ? state.selection.focusBeats
        : state.selection.anchorBeats;
    const nearestEdge =
      furthestEdge === state.selection.focusBeats
        ? state.selection.anchorBeats
        : state.selection.focusBeats;
    return {
      ...state,
      selection: {
        ...state.selection,
        anchorBeats: furthestEdge,
        focusBeats: nearestEdge,
      },
    };
  };

export const selectTrackIds =
  (
    startTrackId: string | null,
    endTrackId: string | null,
    focusedTrackId: string | null,
    isShiftSelect: boolean = false
  ) =>
  (state: StudioProjectState) => {
    let trackIds = state.selection.trackIds;
    const selectableTrackIds = getSelectableTrackIds(state);
    const firstSelectedTrackId = selectableTrackIds.find((trackId) =>
      state.selection.trackIds.includes(trackId)
    );

    // Check if AFTER_LAST_TRACK is in the selection for finding last selected
    const hasAfterLastTrackSelected =
      state.selection.trackIds.includes(AFTER_LAST_TRACK);
    const lastSelectedTrackId = hasAfterLastTrackSelected
      ? AFTER_LAST_TRACK
      : selectableTrackIds.findLast((trackId) =>
          state.selection.trackIds.includes(trackId)
        );

    const effectiveStartTrackId = isShiftSelect
      ? firstSelectedTrackId
      : startTrackId;

    const effectiveEndTrackId = isShiftSelect
      ? lastSelectedTrackId
      : endTrackId;

    if (effectiveStartTrackId && effectiveEndTrackId) {
      if (isShiftSelect) {
        const distanceToFirstTrack = focusedTrackId
          ? Math.abs(
              countTracksBetween(effectiveStartTrackId, focusedTrackId, state)
            )
          : 0;

        const distanceToLastTrack = focusedTrackId
          ? Math.abs(
              countTracksBetween(effectiveEndTrackId, focusedTrackId, state)
            )
          : 0;

        if (
          focusedTrackId &&
          distanceToFirstTrack > 0 &&
          distanceToFirstTrack <= distanceToLastTrack
        ) {
          trackIds = getTrackIdsBetween(
            focusedTrackId,
            effectiveEndTrackId,
            state
          );
        } else if (
          focusedTrackId &&
          distanceToLastTrack > 0 &&
          distanceToLastTrack <= distanceToFirstTrack
        ) {
          trackIds = getTrackIdsBetween(
            effectiveStartTrackId,
            focusedTrackId,
            state
          );
        } else {
          trackIds = getTrackIdsBetween(
            effectiveStartTrackId,
            effectiveEndTrackId,
            state
          );
        }
      } else {
        trackIds = getTrackIdsBetween(
          effectiveStartTrackId,
          effectiveEndTrackId,
          state
        );
      }
    }

    return {
      ...state,
      selection: {
        ...state.selection,
        trackIds,
        focusedTrackId,
      },
    };
  };

export default function dragSelect(
  anchorBeats: number,
  focusBeats: number,
  startTrackId: string | null,
  endTrackId: string | null,
  focusedTrackId: string | null,
  isShiftSelect: boolean = false
) {
  return combineActions(
    (state: StudioProjectState): StudioProjectState => ({
      ...state,
      selection: {
        ...state.selection,
        anchorBeats: isShiftSelect ? state.selection.anchorBeats : anchorBeats,
        focusBeats,
      },
    }),
    selectTrackIds(startTrackId, endTrackId, focusedTrackId, isShiftSelect)
  );
}
