import { ReplaySubject } from "rxjs";
import { map } from "rxjs/operators";

export class MasterCellAsyncRenderManager {
  readonly asyncElementRegistrate$: ReplaySubject<any>;
  readonly asyncElementLayout$: ReplaySubject<any>;
  private asyncElementRegistrationCount: number;
  readonly notifyAllAsyncElementsRendered: () => void;

  constructor(notifyAllAsyncElementsRendered: () => void) {
    this.asyncElementRegistrate$ = new ReplaySubject();
    this.asyncElementLayout$ = new ReplaySubject();
    this.asyncElementRegistrationCount = 0;

    this.notifyAllAsyncElementsRendered = notifyAllAsyncElementsRendered;
  }

  public emitAsyncElementRegistrate = () => {
    this.asyncElementRegistrate$.next(null);

    return this.decrementAsyncElementRegistrationCount;
  };

  public emitAsyncElementLayout = () => {
    this.asyncElementLayout$.next(null);
  };

  public decrementAsyncElementRegistrationCount = () => {
    if (this.asyncElementRegistrationCount > 0) {
      this.asyncElementRegistrationCount -= 1;
    }
  };

  public onCellLayout = () => {
    if (this.asyncElementRegistrationCount === 0) {
      // cell was rendered but no async registrations were occured
      // it means that we have none async elements inside this cell
      this.finishAsyncRendering();
    }
  };

  public start = () => {
    const registrationSubscription = this.asyncElementRegistrate$.subscribe({
      next: () => {
        // count how many registration of async rendering occured
        this.asyncElementRegistrationCount += 1;
      },
    });

    const layoutSubscription = this.asyncElementLayout$
      .pipe(map((_, index) => index + 1))
      .subscribe({
        next: (layoutCount) => {
          if (layoutCount >= this.asyncElementRegistrationCount) {
            // when number of onLayout was reached registration count we could stop
            this.finishAsyncRendering();
          }
        },
      });

    return () => {
      registrationSubscription.unsubscribe();
      layoutSubscription.unsubscribe();
    };
  };

  private finishAsyncRendering() {
    this.notifyAllAsyncElementsRendered();

    // stop waiting async elements
    this.asyncElementRegistrate$.complete();
    this.asyncElementLayout$.complete();
  }
}
