nopaque.resource_lists.ResourceList = class ResourceList {
  /* A wrapper class for the list.js list.
   * This class is not meant to be used directly, instead it should be used as
   * a base class for concrete resource list implementations.
   */

  static htmlClass;

  static defaultOptions = {
    page: 5,
    pagination: {
      innerWindow: 2,
      outerWindow: 2
    }
  };

  constructor(listContainerElement, options = {}) {
    if ('items' in options) {
      throw '"items" is not supported as an option, define it as a getter in the list class';
    }
    if ('valueNames' in options) {
      throw '"valueNames" is not supported as an option, define it as a getter in the list class';
    }
    let _options = nopaque.Utils.mergeObjectsDeep(
      {item: this.item, valueNames: this.valueNames},
      nopaque.resource_lists.ResourceList.defaultOptions,
      options
    );
    this.listContainerElement = listContainerElement;
    this.initListContainerElement();
    this.listjs = new List(listContainerElement, _options);
  }

  add(resources, callback) {
    let tmp = Array.isArray(resources) ? resources : [resources];
    let values = tmp.map((resource) => {
      return this.mapResourceToValue(resource);
    });
    this.listjs.add(values, (items) => {
      this.sort();
      if (typeof callback === 'function') {
        callback(items);
      }
    });
  }

  remove(id) {
    this.listjs.remove('id', id);
  }

  replace(id, key, value) {
    let item = this.listjs.get('id', id)[0];
    item.values({[key]: value});
  }

  // #region Mandatory getters and methods to implement
  get item() {throw 'Not implemented';}

  get valueNames() {throw 'Not implemented';}

  initListContainerElement() {throw 'Not implemented';}
  // #endregion
  
  // #region Optional methods to implement
  mapResourceToValue(resource) {return resource;}

  sort() {return;}
  // #endregion
}