'use strict';

const Npc = require('./Npc');
const BehaviorManager = require('./BehaviorManager');

/**
 * Stores definitions of entities to allow for easy creation/cloning
 */
class EntityFactory {
  constructor() {
    this.entities = new Map();
    this.scripts = new BehaviorManager();
  }

  /**
   * Create the key used by the entities and scripts maps
   * @param {string} areaName
   * @param {number} id
   * @return {string}
   */
  createEntityRef(area, id) {
    return area + ':' + id;
  }

  /**
   * @param {string} entityRef
   * @return {Object}
   */
  getDefinition(entityRef) {
    return this.entities.get(entityRef);
  }

  /**
   * @param {string} entityRef
   * @param {Object} def
   */
  setDefinition(entityRef, def) {
    def.entityReference = entityRef;
    this.entities.set(entityRef, def);
  }

  /**
   * Add an event listener from a script to a specific item
   * @see BehaviorManager::addListener
   * @param {string}   entityRef
   * @param {string}   event
   * @param {Function} listener
   */
  addScriptListener(entityRef, event, listener) {
    this.scripts.addListener(entityRef, event, listener);
  }

  /**
   * Create a new instance of a given npc definition. Resulting npc will not be held or equipped
   * and will _not_ have its default contents. If you want it to also populate its default contents
   * you must manually call `npc.hydrate(state)`
   *
   * @param {Area}   area
   * @param {string} entityRef
   * @param {Class}  Type      Type of entity to instantiate
   * @return {type}
   */
  createByType(area, entityRef, Type) {
    const definition = this.getDefinition(entityRef);
    if (!definition) {
      throw new Error('No Entity definition found for ' + entityRef)
    }
    const entity = new Type(area, definition);

    if (this.scripts.has(entityRef)) {
      this.scripts.get(entityRef).attach(entity);
    }

    return entity;
  }

  create() {
    throw new Error("No type specified for Entity.create");
  }

  /**
   * Clone an existing entity.
   * @param {Item|Npc|Room|Area} entity
   * @return {Item|Npc|Room|Area}
   */
  clone(entity) {
    return this.create(entity.area, entity.entityReference);
  }
}

module.exports = EntityFactory;