import { AsyncAction } from './AsyncAction'; 
 | 
import { Subscription } from '../Subscription'; 
 | 
import { AsyncScheduler } from './AsyncScheduler'; 
 | 
import { SchedulerAction } from '../types'; 
 | 
import { TimerHandle } from './timerHandle'; 
 | 
  
 | 
export class VirtualTimeScheduler extends AsyncScheduler { 
 | 
  /** @deprecated Not used in VirtualTimeScheduler directly. Will be removed in v8. */ 
 | 
  static frameTimeFactor = 10; 
 | 
  
 | 
  /** 
 | 
   * The current frame for the state of the virtual scheduler instance. The difference 
 | 
   * between two "frames" is synonymous with the passage of "virtual time units". So if 
 | 
   * you record `scheduler.frame` to be `1`, then later, observe `scheduler.frame` to be at `11`, 
 | 
   * that means `10` virtual time units have passed. 
 | 
   */ 
 | 
  public frame: number = 0; 
 | 
  
 | 
  /** 
 | 
   * Used internally to examine the current virtual action index being processed. 
 | 
   * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. 
 | 
   */ 
 | 
  public index: number = -1; 
 | 
  
 | 
  /** 
 | 
   * This creates an instance of a `VirtualTimeScheduler`. Experts only. The signature of 
 | 
   * this constructor is likely to change in the long run. 
 | 
   * 
 | 
   * @param schedulerActionCtor The type of Action to initialize when initializing actions during scheduling. 
 | 
   * @param maxFrames The maximum number of frames to process before stopping. Used to prevent endless flush cycles. 
 | 
   */ 
 | 
  constructor(schedulerActionCtor: typeof AsyncAction = VirtualAction as any, public maxFrames: number = Infinity) { 
 | 
    super(schedulerActionCtor, () => this.frame); 
 | 
  } 
 | 
  
 | 
  /** 
 | 
   * Prompt the Scheduler to execute all of its queued actions, therefore 
 | 
   * clearing its queue. 
 | 
   * @return {void} 
 | 
   */ 
 | 
  public flush(): void { 
 | 
    const { actions, maxFrames } = this; 
 | 
    let error: any; 
 | 
    let action: AsyncAction<any> | undefined; 
 | 
  
 | 
    while ((action = actions[0]) && action.delay <= maxFrames) { 
 | 
      actions.shift(); 
 | 
      this.frame = action.delay; 
 | 
  
 | 
      if ((error = action.execute(action.state, action.delay))) { 
 | 
        break; 
 | 
      } 
 | 
    } 
 | 
  
 | 
    if (error) { 
 | 
      while ((action = actions.shift())) { 
 | 
        action.unsubscribe(); 
 | 
      } 
 | 
      throw error; 
 | 
    } 
 | 
  } 
 | 
} 
 | 
  
 | 
export class VirtualAction<T> extends AsyncAction<T> { 
 | 
  protected active: boolean = true; 
 | 
  
 | 
  constructor( 
 | 
    protected scheduler: VirtualTimeScheduler, 
 | 
    protected work: (this: SchedulerAction<T>, state?: T) => void, 
 | 
    protected index: number = (scheduler.index += 1) 
 | 
  ) { 
 | 
    super(scheduler, work); 
 | 
    this.index = scheduler.index = index; 
 | 
  } 
 | 
  
 | 
  public schedule(state?: T, delay: number = 0): Subscription { 
 | 
    if (Number.isFinite(delay)) { 
 | 
      if (!this.id) { 
 | 
        return super.schedule(state, delay); 
 | 
      } 
 | 
      this.active = false; 
 | 
      // If an action is rescheduled, we save allocations by mutating its state, 
 | 
      // pushing it to the end of the scheduler queue, and recycling the action. 
 | 
      // But since the VirtualTimeScheduler is used for testing, VirtualActions 
 | 
      // must be immutable so they can be inspected later. 
 | 
      const action = new VirtualAction(this.scheduler, this.work); 
 | 
      this.add(action); 
 | 
      return action.schedule(state, delay); 
 | 
    } else { 
 | 
      // If someone schedules something with Infinity, it'll never happen. So we 
 | 
      // don't even schedule it. 
 | 
      return Subscription.EMPTY; 
 | 
    } 
 | 
  } 
 | 
  
 | 
  protected requestAsyncId(scheduler: VirtualTimeScheduler, id?: any, delay: number = 0): TimerHandle { 
 | 
    this.delay = scheduler.frame + delay; 
 | 
    const { actions } = scheduler; 
 | 
    actions.push(this); 
 | 
    (actions as Array<VirtualAction<T>>).sort(VirtualAction.sortActions); 
 | 
    return 1; 
 | 
  } 
 | 
  
 | 
  protected recycleAsyncId(scheduler: VirtualTimeScheduler, id?: any, delay: number = 0): TimerHandle | undefined { 
 | 
    return undefined; 
 | 
  } 
 | 
  
 | 
  protected _execute(state: T, delay: number): any { 
 | 
    if (this.active === true) { 
 | 
      return super._execute(state, delay); 
 | 
    } 
 | 
  } 
 | 
  
 | 
  private static sortActions<T>(a: VirtualAction<T>, b: VirtualAction<T>) { 
 | 
    if (a.delay === b.delay) { 
 | 
      if (a.index === b.index) { 
 | 
        return 0; 
 | 
      } else if (a.index > b.index) { 
 | 
        return 1; 
 | 
      } else { 
 | 
        return -1; 
 | 
      } 
 | 
    } else if (a.delay > b.delay) { 
 | 
      return 1; 
 | 
    } else { 
 | 
      return -1; 
 | 
    } 
 | 
  } 
 | 
} 
 |