/**
 * @module meteoJS/synview/collection
 */
import addEventFunctions from '../Events.js';

/**
 * Triggered on adding item to collection.
 * 
 * @event module:meteoJS/synview/collection#add:item
 * @param {object} Added item.
 */

/**
 * Triggered on replacing item with already existing ID.
 * 
 * @event module:meteoJS/synview/collection#replace:item
 * @param {object} Added item.
 * @param {object} Replaced and removed item.
 */

/**
 * Triggered on removing item from collection.
 * 
 * @event module:meteoJS/synview/collection#remove:item
 * @param {object} Removed item.
 */

/**
 * Collection of items.
 * Items have at least a getId() method, which returns a unique ID.
 */
export class Collection {
  
  constructor() {
    /**
     * List of IDs of the items.
     * @member {mixed}
     * @private
     */
    this.itemIds = [];
    
    /**
     * List of items, ID as key of the object.
     * @member {Object}
     * @private
     */
    this.items = {};
  }
  
  /**
   * Returns count of items in this collection.
   * 
   * @return {integer} Count.
   */
  getCount() {
    return this.itemIds.length;
  }
  
  /**
   * Returns items (in order as appended).
   * 
   * @return {Object[]} Items.
   */
  getItems() {
    return this.itemIds.map(function (id) { return this.items[id]; }, this);
  }
  
  /**
   * Returns a list of IDs (in order as appended).
   * 
   * @return {mixed[]} List of IDs.
   */
  getItemIds() {
    return this.itemIds;
  }
  
  /**
   * Returns item by ID, undefined if ID doesn't exist.
   * 
   * @param {mixed} id ID.
   * @return {Object|undefined} Item.
   */
  getItemById(id) {
    return (id in this.items) ? this.items[id] : undefined;
  }
  
  /**
   * Returns if an ID exists in this collection.
   * 
   * @param {mixed} id ID.
   * @return {boolean} If exists.
   */
  containsId(id) {
    return this.getIndexById(id) !== -1;
  }
  
  /**
   * Returns index of the item in this collecition, -1 if not existant.
   * 
   * @param {mixed} id ID.
   * @return {integer} Index.
   */
  getIndexById(id) {
    var result = -1;
    this.itemIds.forEach(function (itemId, i) {
      if (itemId == id)
        result = i;
    });
    return result;
  }
  
  /**
   * Append an item to the collection.
   * 
   * @param {object} item New item.
   * @return {module:meteoJS/synview/collection.Collection} This.
   * @fires module:meteoJS/synview/collection#add:item
   * @fires module:meteoJS/synview/collection#replace:item
   */
  append(item) {
    var id = item.getId();
    if (this.containsId(id)) {
      this.trigger('replace:item', item, this.getItemById(id));
      this.items[id] = item;
    }
    else {
      this.itemIds.push(id);
      this.items[id] = item;
      this.trigger('add:item', item);
    }
    return this;
  }
  
  /**
   * Removes an item from the collection.
   * 
   * @param {mixed} id ID of the item to delete.
   * @return {module:meteoJS/synview/collection.Collection} This.
   * @fires module:meteoJS/synview/collection#remove:item
   */
  remove(id) {
    var item = this.getItemById(id);
    if (item !== undefined) {
      var index = this.getIndexById(id);
      delete this.items[id];
      this.itemIds.splice(index, 1);
      this.trigger('remove:item', item);
    }
    return this;
  }
  
}
addEventFunctions(Collection.prototype);
export default Collection;