import { useEffect, useState } from "react";

export class Sub<T> {
  _listeners: ((obj: T) => void)[] = [];
  _current: T | null = null;

  subscribe(listener: any) {
    if (this._listeners.indexOf(listener) < 0) {
      this._listeners.push(listener);
    }
  }

  unsubscribe(listener: any) {
    const index = this._listeners.indexOf(listener);
    if (index >= 0) {
      this._listeners.splice(index, 1);
    }
  }

  get(): T | null {
    return this._current;
  }

  set(obj: T) {
    this._current = obj;
    this._listeners.forEach((cb) => cb(obj));
  }
}

export function useSub<T>(
  sub: Sub<T>,
  effectChanges: ((v: T | null) => any[]) | null = null
): T | null {
  const [currentState, stateSetter] = useState(sub.get());

  useEffect(
    () => {
      const listener = (obj: T) => stateSetter(obj);
      sub.subscribe(listener);
      return () => sub.unsubscribe(listener);
      // This effect uses "sub" from outside of its scope, should it be a dependency?
      // Ignore the lint warning for now.
      // eslint-disable-next-line
    },
    effectChanges ? effectChanges(currentState) : []
  );

  return currentState;
}
