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
	}

}
