import * as plugins from './smartrx.plugins.js';
import * as rxjs from './smartrx.plugins.rxjs.js';

export interface IEventEmitter<T = any> {
  on: (eventNameArg: string, eventHandlerArg: (eventPayload: T) => any) => void;
}

/**
 * bundles an observable with an emitter
 */
export interface IObservableEventBundle<T> {
  subject: rxjs.Subject<any>;
  eventRef: T;
  event: string;
}

/**
 * manages observables by making sure that only one observable is regsitered per event
 */
export class Observablemap {
  public observableEventEmitterBundleArray = new Array<
    IObservableEventBundle<IEventEmitter<unknown>>
  >();
  public observableEventTargetBundleArray = new Array<IObservableEventBundle<EventTarget>>();

  /**
   * creates a hot subject if not yet registered for the event.
   * In case event has been registered before the same observable is returned.
   */
  public getSubjectForEmitterEvent<T>(
    emitterArg: IEventEmitter<T>,
    eventArg: string
  ): rxjs.Subject<T> {
    const existingBundle = this.observableEventEmitterBundleArray.find((bundleArg) => {
      return bundleArg.eventRef === emitterArg && bundleArg.event === eventArg;
    });
    if (existingBundle) {
      return existingBundle.subject;
    } else {
      const emitterObservable = rxjs.fromEvent<T>(emitterArg as any, eventArg);
      const emitterSubject = new rxjs.Subject();
      emitterObservable.subscribe(emitterSubject);
      const newBundle: IObservableEventBundle<IEventEmitter> = {
        subject: emitterSubject,
        eventRef: emitterArg,
        event: eventArg,
      };
      this.observableEventEmitterBundleArray.push(newBundle);
      return newBundle.subject;
    }
  }

  public getSubjectForEventTarget<T>(
    eventTargetArg: EventTarget,
    eventNameArg: string
  ): rxjs.Subject<T> {
    const existingBundle = this.observableEventTargetBundleArray.find((bundleArg) => {
      return bundleArg.eventRef === eventTargetArg && bundleArg.event === eventNameArg;
    });
    if (existingBundle) {
      return existingBundle.subject;
    } else {
      const emitterSubject = new rxjs.Subject();
      const newBundle: IObservableEventBundle<EventTarget> = {
        subject: emitterSubject,
        eventRef: eventTargetArg,
        event: eventNameArg,
      };
      this.observableEventTargetBundleArray.push(newBundle);
      return newBundle.subject;
    }
  }
}
