import raf from '../../_util/raf';
import type { Ref, UnwrapRef } from 'vue';
import { onBeforeUnmount, ref, shallowRef } from 'vue';

export type Updater<State> = (prev: State) => State;

export function useLayoutState<State>(
  defaultState: State,
): [Ref<State>, (updater: Updater<State>) => void] {
  const stateRef = shallowRef<State>(defaultState);
  let rafId: number;
  const updateBatchRef = shallowRef<Updater<State>[]>([]);
  function setFrameState(updater: Updater<State>) {
    updateBatchRef.value.push(updater);
    raf.cancel(rafId);
    rafId = raf(() => {
      const prevBatch = updateBatchRef.value;
      // const prevState = stateRef.value;
      updateBatchRef.value = [];
      prevBatch.forEach(batchUpdater => {
        stateRef.value = batchUpdater(stateRef.value as State);
      });
    });
  }
  onBeforeUnmount(() => {
    raf.cancel(rafId);
  });

  return [stateRef as Ref<State>, setFrameState];
}

/** Lock frame, when frame pass reset the lock. */
export function useTimeoutLock<State>(
  defaultState?: State,
): [(state: UnwrapRef<State>) => void, () => UnwrapRef<State> | null] {
  const frameRef = ref<State | null>(defaultState || null);
  const timeoutRef = ref<any>();

  function cleanUp() {
    clearTimeout(timeoutRef.value);
  }

  function setState(newState: UnwrapRef<State>) {
    frameRef.value = newState;
    cleanUp();

    timeoutRef.value = setTimeout(() => {
      frameRef.value = null;
      timeoutRef.value = undefined;
    }, 100);
  }

  function getState() {
    return frameRef.value;
  }

  onBeforeUnmount(() => {
    cleanUp();
  });

  return [setState, getState];
}