/**
 *  LogUtility is a wrapper class for console.log/warn/error with vega branding prefix
 */
class LogUtility$1 {
  /**
   * `setVegaLogLevel` sets the log level for the Vega logger
   *
   * @param {LogLevelType} level - The log level to set.
   */
  static setVegaLogLevel(level) {
    this.logLevel = level;
  }
  /**
   * Prints a log message to the console
   *
   * @param {unknown[]} message - The message to log.
   */
  static log(...message) {
    if (this.shouldLog('log')) {
      console.log(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
    }
  }
  /**
   * Prints a warning message to the console
   *
   * @param {unknown[]} message - The message to log.
   */
  static warn(...message) {
    if (this.shouldLog('warn')) {
      console.warn(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
    }
  }
  /**
   * Prints an error message to the console
   *
   * @param {unknown[]} message - The message to be logged.
   */
  static error(...message) {
    if (this.shouldLog('error')) {
      console.error(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
    }
  }
  /**
   * If the current log level is less than or equal to the expected log level, then log
   *
   * @param {LogLevelType} level - LogLevelType - The log level of the message.
   * @returns {boolean} A boolean value.
   */
  static shouldLog(level) {
    const current = this.LOG_LEVELS.findIndex((item) => item === level);
    const expect = this.LOG_LEVELS.findIndex((item) => item === this.logLevel);
    return current <= expect;
  }
}
LogUtility$1.logLevel = 'error';
LogUtility$1.LOG_PREFIX_STR = '[Vega]';
LogUtility$1.LOG_PREFIX_STYLE = 'font-weight: bold';
LogUtility$1.LOG_LEVELS = ['off', 'error', 'warn', 'log'];
/**
 * > Sets the log level for the Vega library
 *
 * @param {LogLevelType} level - The log level to set.
 * @returns {void} -
 */
function setVegaLogLevel(level) {
  LogUtility$1.setVegaLogLevel(level);
}

/**
 *  LogUtility is a wrapper class for console.log/warn/error with vega branding prefix
 */
class LogUtility {
  /**
   * Prints a log message to the console
   *
   * @param {unknown[]} message - The message to log.
   */
  static log(...message) {
    console.log(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
  }
  /**
   * Prints a warning message to the console
   *
   * @param {unknown[]} message - The message to log.
   */
  static warn(...message) {
    console.warn(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
  }
  /**
   * Prints an error message to the console
   *
   * @param {unknown[]} message - The message to be logged.
   */
  static error(...message) {
    console.error(`%c${this.LOG_PREFIX_STR}`, this.LOG_PREFIX_STYLE, ...message);
  }
}
LogUtility.LOG_PREFIX_STR = '[Vega]';
LogUtility.LOG_PREFIX_STYLE = 'font-weight: bold';

const SlimmerMappedMethod = Symbol('ProcessorInternalCallback');
const SlimmerConsumedBy = Symbol('SlimmerConsumedBy');
const ConsumerSlimmers = Symbol('ConsumerSlimmers');
const CurrentlyInvokedMethodName = Symbol('CurrentlyInvokedMethodName');

/**
 * Decorator to map a slimmer field to a component field, which means:
 * - If you get the slimmer field value, it will return the corresponding component field value.
 * - If you set the slimmer field to some value, it will set the corresponding component field to the same value (if not readonly).
 *
 * We will use the component field name as the default value for the slimmer field name.
 *
 * @param {MapToComponentFieldOptions} options The options for the decorator
 * @returns {Function} The decorator function
 */
function MapToComponentField(options) {
  return function (slimmerPrototype, propertyKey) {
    Object.defineProperty(slimmerPrototype, propertyKey, {
      get: createSlimmerMappingFieldGetter(propertyKey, options),
      set: createSlimmerMappingFieldSetter(propertyKey, options),
      configurable: true,
    });
  };
}
/**
 * Helper function to create a setter for a slimmer field that maps to the injecting component field.
 * We will use the component field name as the default value for the slimmer field name.
 *
 * @param {string} slimmerFieldName The name of the slimmer field
 * @param {MapToComponentFieldOptions} options The options of the slimmer field
 * @returns {Function} A setter for the slimmer field
 */
function createSlimmerMappingFieldSetter(slimmerFieldName, options = {}) {
  return function (value) {
    var _a;
    if (!options.writable) {
      LogUtility.error(`${slimmerFieldName} is not writable`);
      return;
    }
    const dependentComponent = this[SlimmerConsumedBy];
    const fieldName = (_a = options.componentFieldName) !== null && _a !== void 0 ? _a : slimmerFieldName;
    dependentComponent[fieldName] = value;
  };
}
/**
 * Helper function to create a getter for a slimmer field that maps to the injecting component field.
 * We will use the component field name as the default value for the slimmer field name.
 *
 * @param {string} slimmerFieldName The name of the slimmer field
 * @param {MapToComponentFieldOptions} options The options of the slimmer field
 * @returns {Function} A getter for the slimmer field
 */
function createSlimmerMappingFieldGetter(slimmerFieldName, options = {}) {
  return function () {
    const { componentFieldName } = options;
    const dependentComponent = this[SlimmerConsumedBy];
    const fieldName = componentFieldName !== null && componentFieldName !== void 0 ? componentFieldName : slimmerFieldName;
    return dependentComponent[fieldName];
  };
}

/**
 * The function `MapToComponentMethod` is a TypeScript decorator that maps a method in a class to a
 * method in a component.
 *
 * @param {string} componentMethodName - The componentMethodName parameter is a string that represents
 * the name of the method in the component that you want to map to.
 * @returns A property decorator function is being returned.
 */
function MapToComponentMethod(componentMethodName) {
  return function (processorPrototype, propertyKey) {
    initPrototypeSlimmerMappedMethodArray(processorPrototype);
    processorPrototype[SlimmerMappedMethod].push({
      componentMethodName,
      slimmerMethodName: propertyKey,
    });
  };
}
/**
 * The function initializes a property on a class prototype called "SlimmerMappedMethod" as an array if it doesn't already exist.
 * Use the `hasOwnProperty` method make sure the child slimmer not effect the parent slimmer
 *
 * @param {ClassPrototype<VegaSlimmer>} processorPrototype -  It represents the prototype of a class called `VegaSlimmer`.
 */
function initPrototypeSlimmerMappedMethodArray(processorPrototype) {
  if (!Object.prototype.hasOwnProperty.call(processorPrototype, SlimmerMappedMethod)) {
    processorPrototype[SlimmerMappedMethod] = (processorPrototype[SlimmerMappedMethod] ? [].concat(processorPrototype[SlimmerMappedMethod]) : []);
  }
}

/**
 * This decorator is used to re-define a field in a vega component
 * which will be used for injecting the VegaSlimmer into the vega component.
 *
 * @returns {Function} The decorator function.
 */
function InjectVegaSlimmer() {
  return function (componentPrototype, propertyKey) {
    Object.defineProperty(componentPrototype, propertyKey, {
      set: createSetterForVegaSlimmerField(propertyKey),
      get: createGetterForVegaSlimmerField(propertyKey),
    });
  };
}
/**
 * Creates a setter for a vega slimmer field which will be used for injecting the VegaSlimmer into the vega component.
 *
 * @param {string} fieldName The name of the field to inject the VegaSlimmer into.
 * @returns {Function} A setter for the vega slimmer field.
 */
function createSetterForVegaSlimmerField(fieldName) {
  return function (slimmer) {
    if (!this[ConsumerSlimmers]) {
      this[ConsumerSlimmers] = new Map();
    }
    this[ConsumerSlimmers].set(fieldName, slimmer);
    slimmer[SlimmerConsumedBy] = this;
    registerSlimMethodsToComponent(slimmer, this);
  };
}
/**
 * Creates a getter for a vega slimmer field which will be used for injecting the VegaSlimmer into the vega component.
 *
 * @param {string} fieldName The name of the field to inject the VegaSlimmer into.
 * @returns {Function} A getter for the vega slimmer field.
 */
function createGetterForVegaSlimmerField(fieldName) {
  return function () {
    return this[ConsumerSlimmers].get(fieldName);
  };
}
/**
 * Given a VegaSlimmer and a component, register the slimmer methods to the component.
 * This is done by appending the slimmer methods to the end of the corresponding component method.
 *
 * @param {VegaSlimmer} slimmer The slimmer to register the slimmer method to.
 * @param {ComponentInterface} component The component to register the slimmer method to.
 * @returns {void}
 */
function registerSlimMethodsToComponent(slimmer, component) {
  const slimmerPrototype = slimmer.constructor.prototype;
  if (slimmerPrototype[SlimmerMappedMethod]) {
    for (const entry of slimmerPrototype[SlimmerMappedMethod]) {
      component[entry.componentMethodName] = createRegisteredSlimMethod(component, slimmer, component[entry.componentMethodName], entry);
    }
  }
}
/**
 * Append the slimmer methods to the end of the corresponding component method.
 * Which means the slimmer method will be called when the original method is invoked in the component.
 *
 * @param {ComponentInterface} component The component to register the slimmer method to.
 * @param {VegaSlimmer} slimmer The slimmer to register the slimmer method to.
 * @param {Function} originalMethod The original method to override.
 * @param {SlimmerMappedMethodEntry} mapEntry The map entry for the slimmer method.
 * @returns {Function} The registered slimmer method.
 */
function createRegisteredSlimMethod(component, slimmer, originalMethod, mapEntry) {
  return function (...args) {
    const result = originalMethod === null || originalMethod === void 0 ? void 0 : originalMethod.apply(component, args);
    if (result && typeof result.then === 'function') {
      return result.then(() => slimmer[mapEntry.slimmerMethodName].call(slimmer, ...args));
    }
    else {
      return slimmer[mapEntry.slimmerMethodName].call(slimmer, ...args);
    }
  };
}

/**
 * An abstract class VegaSlimmer
 */
class VegaSlimmer {
}

class GlobalSlimmersRegistry {
  static register(slimmers) {
    this.globalSlimmers = slimmers;
  }
}
GlobalSlimmersRegistry.globalSlimmers = [];

export { ConsumerSlimmers as C, GlobalSlimmersRegistry as G, InjectVegaSlimmer as I, LogUtility$1 as L, MapToComponentMethod as M, SlimmerConsumedBy as S, VegaSlimmer as V, SlimmerMappedMethod as a, CurrentlyInvokedMethodName as b, MapToComponentField as c, createSetterForVegaSlimmerField as d, createGetterForVegaSlimmerField as e, setVegaLogLevel as s };
