'use strict';

/**
 * Representation of a `Character` or container `Item` inventory
 * @extends Map
 */
class Inventory extends Map {
  /**
   * @param {object} init
   * @param {Array<Item>} init.items
   * @param {number} init.max Max number of items this inventory can hold
   */
  constructor(init) {
    init = Object.assign({
      items: [],
      max: Infinity
    }, init);

    super(init.items);
    this.maxSize = init.max;
  }

  /**
   * @param {number} size
   */
  setMax(size) {
    this.maxSize = size;
  }

  /**
   * @return {number}
   */
  getMax() {
    return this.maxSize;
  }

  /**
   * @return {boolean}
   */
  get isFull() {
    return this.size >= this.maxSize;
  }

  /**
   * @param {Item} item
   */
  addItem(item) {
    if (this.isFull) {
      throw new InventoryFullError();
    }
    this.set(item.uuid, item);
  }

  /**
   * @param {Item} item
   */
  removeItem(item) {
    this.delete(item.uuid);
  }

  serialize() {
    // Item is imported here to prevent circular dependency with Item having an Inventory
    const Item = require('./Item');

    let data = {
      items: [],
      max: this.maxSize
    };

    for (const [uuid, item] of this) {
      if (!(item instanceof Item)) {
        this.delete(uuid);
        continue;
      }

      data.items.push([uuid, item.serialize()]);
    }

    return data;
  }

  /**
   * @param {GameState} state
   * @param {Character|Item} carriedBy
   */
  hydrate(state, carriedBy) {
    // Item is imported here to prevent circular dependency with Item having an Inventory
    const Item = require('./Item');

    for (const [uuid, def] of this) {
      if (def instanceof Item) {
        def.carriedBy = carriedBy;
        continue;
      }

      if (!def.entityReference) {
        continue;
      }

      const area = state.AreaManager.getAreaByReference(def.entityReference);
      let newItem = state.ItemFactory.create(area, def.entityReference);
      newItem.uuid = uuid;
      newItem.carriedBy = carriedBy;
      newItem.initializeInventory(def.inventory);
      newItem.hydrate(state, def);
      this.set(uuid, newItem);
      state.ItemManager.add(newItem);
    }
  }
}

/**
 * @extends Error
 */
class InventoryFullError extends Error {}

module.exports = { Inventory, InventoryFullError };