import {Injectable} from '@angular/core';

import {Subject} from 'rxjs';

import {NotifierAction} from '../models/notifier-action.model';

/**
 * Notifier queue service
 *
 * In general, API calls don't get processed right away. Instead, we have to queue them up in order to prevent simultanious API calls
 * interfering with each other. This, at least in theory, is possible at any time. In particular, animations - which potentially overlap -
 * can cause changes in JS classes as well as affect the DOM. Therefore, the queue service takes all actions, puts them in a queue, and
 * processes them at the right time (which is when the previous action has been processed successfully).
 *
 * Technical sidenote:
 * An action looks pretty similar to the ones within the Flux / Redux pattern.
 */
@Injectable()
export class NotifierQueueService {

  /**
   * Stream of actions, subscribable from outside
   */
  public readonly actionStream: Subject<NotifierAction>;

  /**
   * Queue of actions
   */
  private actionQueue: Array<NotifierAction>;

  /**
   * Flag, true if some action is currently in progress
   */
  private isActionInProgress: boolean;

  /**
   * Constructor
   */
  public constructor() {
    this.actionStream = new Subject<NotifierAction>();
    this.actionQueue = [];
    this.isActionInProgress = false;
  }

  /**
   * Push a new action to the queue, and try to run it
   *
   * @param action Action object
   */
  public push(action: NotifierAction): void {
    this.actionQueue.push(action);
    this.tryToRunNextAction();
  }

  /**
   * Continue with the next action (called when the current action is finished)
   */
  public continue(): void {
    this.isActionInProgress = false;
    this.tryToRunNextAction();
  }

  /**
   * Try to run the next action in the queue; we skip if there already is some action in progress, or if there is no action left
   */
  private tryToRunNextAction(): void {
    if (this.isActionInProgress || this.actionQueue.length === 0) {
      return; // Skip (the queue can now go drink a coffee as it has nothing to do anymore)
    }

    this.isActionInProgress = true;
    this.actionStream.next(this.actionQueue.shift()); // Push next action to the stream, and remove the current action from the queue
  }

}
