import * as d3 from 'd3';

export interface Pointer {
  id: number;
  point: [number, number] | null;
  prev: [number, number] | null;
  sourceEvent?: PointerEvent;
}

export interface PointerOptions {
  start?: (tracker: Pointer) => void;
  move?: (tracker: Pointer) => void;
  out?: (tracker: Pointer) => void;
  end?: (tracker: Pointer) => void;
}

export function trackPointer(event: PointerEvent, { start, move, out, end }: PointerOptions) {
  const tracker: Pointer = {
    id: event.pointerId,
    point: null,
    prev: null,
  };

  const id = event.pointerId;
  const target = event.target as Element;
  tracker.point = d3.pointer(event, target);
  target.setPointerCapture(id);

  d3.select(target)
    .on(`pointerup.${ id } pointercancel.${ id } lostpointercapture.${ id }`, (sourceEvent: PointerEvent) => {
      if (sourceEvent.pointerId !== id) {
        return;
      }

      tracker.sourceEvent = sourceEvent;
      d3.select(target).on(`.${ id }`, null);
      target.releasePointerCapture(id);
      end?.(tracker);
    })
    .on(`pointermove.${ id }`, (sourceEvent) => {
      if (sourceEvent.pointerId !== id) {
        return;
      }
      tracker.sourceEvent = sourceEvent;
      tracker.prev = tracker.point;
      tracker.point = d3.pointer(sourceEvent, target);
      move?.(tracker);
    })
    .on(`pointerout.${ id }`, (e) => {
      if (e.pointerId !== id) {
        return;
      }
      tracker.sourceEvent = e;
      tracker.point = null;
      out?.(tracker);
    });

  start?.(tracker);

  return [ 'pointerup', 'pointercancel', 'lostpointercapture', 'pointermove', 'pointerout' ].map((event) => `${ event }.${ id }`);
}
