import { makeLog } from "../../utils/devOnly";
import { cancelIdleCb, reqIdleCb } from "./IdleDeadline";
const log = makeLog('DeferValue');

/**
 *  initializ value when idle.
 * @author: Aamir khan
 * @example 
 * const formatter = new DeferValue(() => {
    return new Intl.DateTimeFormat('en-US', {
      timeZone: 'America/Los_Angeles',
    });
  });

  console.log(this.formatter.getValue().format(new Date()));
  
  Note: If you’re in a situation where you want to use the DeferValue class but you can’t change your public API,
  you can use the DeferValue with getters or use `defineDeferProp`
 */
class DeferValue {
  /**
   * Accepts a function to initialize the value of a variable when idle.
   * @param {Function} init
   */
  constructor(init) {
    this.init_ = init;
    log('making a new defer value');

    /** @private */
    this.value_ = undefined;


    /** @private */
    this.idleHandle_ = reqIdleCb(() => {
      this.value_ = this.init_();
      log('idleHandle called', this.value_);
    });
  }

  /**
   * Returns the value if it's already been initialized. If it hasn't then the
   * initializer function is run immediately and the pending idle callback
   * is cancelled.
   * @return {?}  depends on the init function
   */
  getValue() {
    if (this.value_ === undefined) {
      log('DeferValue is undefined');
      this.cancleIdleInit_();
      this.value_ = this.init_();
    }

    log('getValue called', this.value_);
    return this.value_;
  }

  /**
   * Cancels any scheduled requestIdleCallback
   * and sets the new value as defer value
   * @param {?} newValue value for the DeferValue
   */
  setValue(newValue) {
    this.cancleIdleInit_();
    this.value_ = newValue;
  }

  /**
   * Cancels any scheduled requestIdleCallback and resets the handle.
   * @private
   */
  cancleIdleInit_() {
    if (this.idleHandle_) {
      cancelIdleCb(this.idleHandle_);
      this.idleHandle_ = null;
      log('cancleIdleInit called');
    }
  }
}

export default DeferValue;