import {
  SPEED_MAXIMUM,
  SPEED_MINIMUM,
} from '@/components/focusedObjectPanel/StudioClipSettingsSubsection';

import {
  getFocusedStudioClip,
  getSelectedClips,
  getSelectionEndBeats,
  getSelectionStartBeats,
  getStudioClipsById,
} from '../selectors';
import { StudioClip, StudioProjectState } from '../types';
import { combineActions } from './combineActions';
import updateClip from './updateClip';

const updateSingleClipSpeed = (id: string, speed: number, delta?: boolean) => {
  return updateClip(id, (studioClip) => {
    const durationBeats = studioClip.endBeats - studioClip.startBeats;
    const newSpeed = Math.max(
      SPEED_MINIMUM,
      Math.min(SPEED_MAXIMUM, delta ? studioClip.warp.speed * speed : speed)
    );
    const speedMultiplier = newSpeed / studioClip.warp.speed;
    return {
      ...studioClip,
      warp: {
        ...studioClip.warp,
        speed: newSpeed,
      },
      readStartBeats: studioClip.readStartBeats / speedMultiplier,
      endBeats: studioClip.startBeats + durationBeats / speedMultiplier,
    };
  });
};

export default function updateClipSpeed(
  studioClipIds: string[],
  speed: number,
  delta?: boolean
) {
  return (state: StudioProjectState): StudioProjectState => {
    const clipsById = getStudioClipsById(state);
    const targetClips = studioClipIds
      .map((id) => clipsById[id])
      .filter(Boolean) as StudioClip[];
    const focusedClip = getFocusedStudioClip(state);
    return combineActions(
      ...targetClips.map((c) => updateSingleClipSpeed(c.id, speed, delta)),
      (state) => ({
        ...state,
        selection: focusedClip
          ? {
              ...state.selection,
              anchorBeats: getStudioClipsById(state)[focusedClip.id].startBeats,
              focusBeats: getStudioClipsById(state)[focusedClip.id].endBeats,
            }
          : {
              ...state.selection,
              anchorBeats: Math.min(
                ...getSelectedClips(state).map((c) => c.startBeats),
                getSelectionStartBeats(state)
              ),
              focusBeats: Math.max(
                ...getSelectedClips(state).map((c) => c.endBeats),
                getSelectionEndBeats(state)
              ),
            },
      })
    )(state);
  };
}
