/**
 * Wraps the WiregasmLib lib functionality and manages a single DissectSession
 */
class Wiregasm {
  constructor() {
    this.initialized = false;
    this.session = null;
  }
  /**
   * Initialize the wrapper and the Wiregasm module
   *
   * @param loader Loader function for the Emscripten module
   * @param overrides Overrides
   */
  async init(loader, overrides = {}, beforeInit = null) {
    if (this.initialized) {
      return;
    }
    this.lib = await loader(overrides);
    this.uploadDir = this.lib.getUploadDirectory();
    this.pluginsDir = this.lib.getPluginsDirectory();
    if (beforeInit !== null) {
      await beforeInit(this.lib);
    }
    this.lib.init();
    this.initialized = true;
  }
  list_modules() {
    return this.lib.listModules();
  }
  list_prefs(module) {
    return this.lib.listPreferences(module);
  }
  apply_prefs() {
    this.lib.applyPreferences();
  }
  set_pref(module, key, value) {
    const ret = this.lib.setPref(module, key, value);
    if (ret.code != PrefSetResult.PREFS_SET_OK) {
      const message =
        ret.error != '' ? ret.error : preferenceSetCodeToError(ret.code);
      throw new Error(
        `Failed to set preference (${module}.${key}): ${message}`
      );
    }
  }
  get_pref(module, key) {
    const response = this.lib.getPref(module, key);
    if (response.code != 0) {
      throw new Error(`Failed to get preference (${module}.${key})`);
    }
    return response.data;
  }
  /**
   * Check the validity of a filter expression.
   *
   * @param filter A display filter expression
   */
  test_filter(filter) {
    return this.lib.checkFilter(filter);
  }
  complete_filter(filter) {
    const out = this.lib.completeFilter(filter);
    return {
      err: out.err,
      fields: vectorToArray(out.fields),
    };
  }
  reload_lua_plugins() {
    this.lib.reloadLuaPlugins();
  }
  add_plugin(name, data, opts = {}) {
    const path = this.pluginsDir + '/' + name;
    this.lib.FS.writeFile(path, data, opts);
  }
  /**
   * Load a packet trace file for analysis.
   *
   * @returns Response containing the status and summary
   */
  load(name, data, opts = {}) {
    if (this.session != null) {
      this.session.delete();
    }
    const path = this.uploadDir + '/' + name;
    this.lib.FS.writeFile(path, data, opts);
    this.session = new this.lib.DissectSession(path);
    return this.session.load();
  }
  /**
   * Get Packet List information for a range of packets.
   *
   * @param filter Output those frames that pass this filter expression
   * @param skip Skip N frames
   * @param limit Limit the output to N frames
   */
  frames(filter, skip = 0, limit = 0) {
    return this.session.getFrames(filter, skip, limit);
  }
  /**
   * Get full information about a frame including the protocol tree.
   *
   * @param number Frame number
   */
  frame(num) {
    return this.session.getFrame(num);
  }
  follow(follow, filter) {
    return this.session.follow(follow, filter);
  }
  destroy() {
    if (this.initialized) {
      if (this.session !== null) {
        this.session.delete();
        this.session = null;
      }
      this.lib.destroy();
      this.initialized = false;
    }
  }
  /**
   * Returns the column headers
   */
  columns() {
    const vec = this.lib.getColumns();
    // convert it from a vector to array
    return vectorToArray(vec);
  }
}

/**
 * Converts a Vector to a JS array
 *
 * @param vec Vector
 * @returns JS array of the Vector contents
 */
function vectorToArray(vec) {
  return new Array(vec.size()).fill(0).map((_, id) => vec.get(id));
}
function preferenceSetCodeToError(code) {
  switch (code) {
    case PrefSetResult.PREFS_SET_SYNTAX_ERR:
      return 'Syntax error in string';
    case PrefSetResult.PREFS_SET_NO_SUCH_PREF:
      return 'No such preference';
    case PrefSetResult.PREFS_SET_OBSOLETE:
      return 'Preference used to exist but no longer does';
    default:
      return 'Unknown error';
  }
}

if (typeof exports === 'object' && typeof module === 'object') {
  module.exports = Wiregasm;
  module.exports = vectorToArray;
} else if (typeof define === 'function' && define['amd']) {
  define([], function () {
    return Wiregasm;
  });
  define([], function () {
    return vectorToArray;
  });
} else if (typeof exports === 'object') {
  exports['loadWiregasm'] = Wiregasm;
  exports['vectorToArray'] = vectorToArray;
}
